rinha2-back-end-dotnet

Home

C#/.NET 9 Native AOT implementation for the Rinha de Backend 2024/Q1 challenge. Manages a fictional bank API with transaction processing and balance statements under strict resource constraints (1.5 CPU, 550MB RAM total across all containers).

Wiki Pages

Page Description
Challenge What is Rinha de Backend 2024/Q1
Architecture Stack, services, resource constraints
Getting Started Prerequisites and how to run
Performance Results, benchmarks, resource usage
CI/CD Pipeline GitHub Actions workflows

Key Features

  • .NET 9 with Native AOT compilation for zero-JIT startup
  • System.Text.Json source generators for reflection-free serialization
  • Npgsql 10.0.2 with connection pooling and multiplexing
  • PostgreSQL stored procedures for server-side business logic
  • All requests under 800ms at 250MB RAM usage
  • Perfect Score badge achieved

GitHub · Jonathan Peris

Architecture

Overview

The system follows a shared architecture across all Rinha de Backend implementations: two API instances behind an Nginx reverse proxy, with a single PostgreSQL database and an observability stack.

Services

Service Role CPU RAM
webapi1 .NET 9 Native AOT API instance 0.4 100MB
webapi2 .NET 9 Native AOT API instance 0.4 100MB
nginx Reverse proxy / load balancer (least-conn) 0.2 20MB
postgresql Database with stored procedures 0.5 330MB
k6 Load testing (not counted) (not counted)
grafana + influxdb Observability dashboards (not counted) (not counted)

Load Balancing

Nginx uses least_conn strategy to distribute requests across the two API instances.

Database

Business logic is implemented in PostgreSQL stored procedures. The database is tuned for maximum write performance:

  • synchronous_commit=0 — no wait for WAL flush
  • fsync=0 — skip fsync on writes
  • full_page_writes=0 — skip full page writes

Implementation Details

  • Native AOT compilation for zero-JIT startup and minimal memory footprint
  • System.Text.Json source generators for reflection-free JSON serialization
  • Npgsql 10.0.2 with connection pooling and multiplexing for high-throughput database access
  • Conditional compilation to strip OpenTelemetry in production builds
  • Multi-stage Docker build for minimal container image size (amd64, arm64/v8)

Challenge

Rinha de Backend 2024/Q1

The Rinha de Backend is a Brazilian backend programming challenge. The 2024/Q1 edition simulates a fictional bank called "Rinha Financeira" that manages up to 5 named clients, each seeded at startup with a credit limit and initial balance.

Endpoints

Two API endpoints are required:

Endpoint Method Description
/clientes/{id}/transacoes POST Submit a debit or credit transaction for a client (IDs 1–5)
/clientes/{id}/extrato GET Get a client's current balance, credit limit, and recent transactions

Constraints

The challenge imposes strict resource limits across all containers combined:

  • 1.5 CPU total shared across all services
  • 550MB RAM total shared across all services
  • The system is stress tested using Grafana k6 with concurrent users submitting transactions and querying statements

Source

Full specification: github.com/zanfranceschi/rinha-de-backend-2024-q1

CI/CD Pipeline

Workflows

This repository uses four GitHub Actions workflows:

build-check.yml

  • Trigger: Pull requests to main, push to main
  • Steps: Builds the API (Release, AOT=true) and runs a Docker health check to verify the service starts correctly
  • Purpose: Catch build failures and regressions before merging

main-release.yml

  • Trigger: Push to main branch
  • Steps: Builds a multi-platform Docker image (amd64, arm64/v8), pushes it to GitHub Container Registry (GHCR), and runs k6 load tests
  • Purpose: Automated release of production-ready container images with stress test validation

codeql.yml

  • Trigger: Pull requests to main, push to main, weekly schedule
  • Steps: Runs GitHub CodeQL security analysis for C#
  • Purpose: Automated security vulnerability detection

deploy.yml

  • Trigger: Push to main branch
  • Steps: Installs dependencies, builds the Astro site in docs/, and deploys the output to GitHub Pages
  • Purpose: Publish project documentation and stress test reports to GitHub Pages

Docker Image

Published to ghcr.io/jonathanperis/rinha2-back-end-dotnet:latest (amd64, arm64/v8)

Getting Started

Prerequisites

  • Docker with Docker Compose

Clone and Run

git clone https://github.com/jonathanperis/rinha2-back-end-dotnet.git
cd rinha2-back-end-dotnet
docker compose up nginx -d --build

Access

The API is available at http://localhost:9999

Endpoints

Endpoint Method Description
/clientes/{id}/transacoes POST Submit debit or credit transaction
/clientes/{id}/extrato GET Get account balance statement
/healthz GET Health check

Example Requests

Create Transaction

curl -X POST http://localhost:9999/clientes/1/transacoes \
  -H "Content-Type: application/json" \
  -d '{"valor": 1000, "tipo": "c", "descricao": "deposito"}'

Get Statement

curl http://localhost:9999/clientes/1/extrato

Performance

Resource Constraints

The challenge allows a total of 1.5 CPU and 550MB RAM across all containers.

Actual Usage

Metric Limit Actual Margin
RAM 550MB ~250MB 60% below limit
Response time < 800ms All requests

Results

  • All requests completed under 800ms
  • Total RAM usage of approximately 250MB, which is 60% below the 550MB limit
  • Perfect Score badge achieved
  • Native AOT eliminates JIT warm-up latency for consistent cold-start performance

Stress Testing

Load tests are run using the shared rinha2-back-end-k6 test suite, which simulates concurrent users performing debits, credits, validations, and statement queries.