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
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 flushfsync=0— skip fsync on writesfull_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.