You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+55-37Lines changed: 55 additions & 37 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,27 +10,27 @@
10
10
11
11
## About
12
12
13
-
A Rust implementation of the Brazilian backend challenge Rinha de Backend 2024/Q1, where a fictional bank API must handle concurrent transactions under strict resource constraints (1.5 CPU, 550MB RAM total). Built as a minimal single-file API (~140 lines) using Actix-web 4 with Tokio async runtime and SQLx 0.8 for compile-time checked PostgreSQL queries.
13
+
A Rust implementation of the Brazilian backend challenge Rinha de Backend 2024/Q1, where a fictional bank API must handle concurrent transactions under strict resource constraints (1.5 CPU, 550MB RAM total). The current API is a single`src/WebApi/main.rs` entrypoint (173 total lines at this revision) using Actix-web 4 with the Tokio runtime and SQLx 0.8 for PostgreSQL access.
14
14
15
15
## Tech Stack
16
16
17
-
| Technology | Version | Purpose |
18
-
|-----------|---------|---------|
19
-
| Rust |1.94| API implementation |
17
+
| Technology | Version / Source | Purpose |
18
+
|-----------|------------------|---------|
19
+
| Rust |edition 2024; Docker builder `rust:1.95`; CI uses `dtolnay/rust-toolchain@stable`| API implementation |
- Multi-platform Docker image (amd64/arm64) published to GHCR
56
-
-Observability stack with Prometheus, Grafana, and InfluxDB
57
-
-All requests under 800ms at 250MB RAM usage (60% below limit)
48
+
-Single Rust API entrypoint (`src/WebApi/main.rs`, 173 total lines at this revision)
49
+
-Actix-web route handlers for transactions, statements, and `/healthz`
50
+
- SQLx compile-time query validation with offline cache (`src/WebApi/.sqlx/`)
51
+
- PostgreSQL stored procedures for server-side balance updates and statement aggregation
52
+
- UNLOGGED `Clientes` and `Transacoes`tables for challenge-oriented write throughput
53
+
- Lazy static `HashMap` for the five seeded client limits (no DB round-trip for client existence checks)
54
+
- PostgreSQL tuned for benchmark throughput: `synchronous_commit=0`, `fsync=0`, `full_page_writes=0`
55
+
- Multi-platform GHCR release flow: `latest` is assembled as a multi-arch manifest from amd64 and arm64 builds; `latest-arm64` is also pushed during the arm64 leg
56
+
-Development observability stack with Prometheus, Grafana, InfluxDB, and postgres-exporter
57
+
-Committed stress-test report artifacts under `docs/public/reports/`; treat generated reports, not hard-coded README numbers, as the performance source of truth
58
58
59
59
## Getting Started
60
60
@@ -70,50 +70,68 @@ cd rinha2-back-end-rust
70
70
docker compose up nginx -d --build
71
71
```
72
72
73
-
API available at `http://localhost:9999`
73
+
API available at `http://localhost:9999`.
74
+
75
+
The dev compose file also exposes the API instances directly on `localhost:6968` and `localhost:6969`; use the NGINX port (`9999`) for challenge-compatible requests.
74
76
75
77
### API Endpoints
76
78
77
79
| Method | Path | Status Codes | Description |
78
80
|--------|------|-------------|-------------|
79
-
| POST |`/clientes/{id}/transacoes`| 200, 404, 422 | Submit debit or credit transaction |
80
-
| GET |`/clientes/{id}/extrato`| 200, 404 | Get account balance statement |
81
-
| GET |`/healthz`| 200 | Health check |
81
+
| POST |`/clientes/{id}/transacoes`| 200, 404, 422 | Submit debit or credit transaction for client IDs 1-5 |
82
+
| GET |`/clientes/{id}/extrato`| 200, 404 | Get account balance statement with the 10 most recent transactions |
83
+
| GET |`/healthz`| 200 | Health check returning `Healthy`|
84
+
85
+
Transaction payload validation is handled in Rust before the stored procedure call: `tipo` must be `c` or `d`, `descricao` must be non-empty and at most 10 characters, and `valor` must be positive.
82
86
83
87
### Run Stress Tests
84
88
85
89
```bash
86
90
docker compose up k6 --build --force-recreate
87
91
```
88
92
93
+
The dev compose path runs k6 in `MODE=dev` with InfluxDB/Grafana export. The production compose file used by the release workflow runs k6 in `MODE=prod` and exports an HTML report artifact.
README and wiki facts that mirror code, Docker, Compose, or workflow state are checked by:
128
+
129
+
```bash
130
+
python3 scripts/check_docs_drift.py
131
+
```
132
+
133
+
The guard verifies high-value source-backed facts such as API line count, Docker builder version, Rust edition, endpoint coverage, workflow triggers, wiki navigation coverage, and known-stale phrases.
Copy file name to clipboardExpand all lines: docs/wiki/architecture.md
+29-14Lines changed: 29 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,34 +2,49 @@
2
2
3
3
## Overview
4
4
5
-
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.
5
+
The system follows the Rinha de Backend topology used by this repository: two Rust/Actix-web API containers behind NGINX, one PostgreSQL database, and optional observability/load-test services in the development compose file.
|`k6`| Shared load-test runner (`MODE=dev` in dev compose, `MODE=prod` in prod compose) | not counted | not counted |
16
+
|`prometheus`, `grafana`, `influxdb`, `postgres-exporter`| Development observability stack | not counted | not counted |
17
+
18
+
The compose CPU/RAM limits for the challenge services total 1.5 CPU and 550MB.
17
19
18
20
## Load Balancing
19
21
20
-
Nginx uses `least_conn` strategy to distribute requests across the two API instances.
22
+
NGINX listens on `:9999` and proxies all routes to an upstream named `api` with `least_conn` across `webapi1-rust:8080` and `webapi2-rust:8080`. The dev compose file also maps the two API containers to host ports `6968` and `6969`, but challenge-compatible traffic should go through NGINX.
23
+
24
+
## API Runtime
25
+
26
+
The API is a single Rust entrypoint (`src/WebApi/main.rs`, 173 total lines at this revision):
27
+
28
+
-`GET /clientes/{id}/extrato` validates the client ID against a lazy static `HashMap`, then calls `GetSaldoClienteById($1)`.
29
+
-`POST /clientes/{id}/transacoes` validates the client ID and payload (`tipo`, `descricao`, `valor`), then calls `InsertTransacao($1, $2, $3, $4)`.
30
+
-`GET /healthz` returns `Healthy` for compose and CI smoke checks.
31
+
- The SQLx pool is created from `DATABASE_URL` with `max_connections(5)` per API instance.
21
32
22
33
## Database
23
34
24
-
Business logic is implemented in PostgreSQL stored procedures. The database is tuned for maximum write performance:
35
+
Business logic is implemented in PostgreSQL stored procedures over UNLOGGED tables:
36
+
37
+
-`Clientes` stores the five seeded clients and their current balance (`SaldoInicial`).
38
+
-`Transacoes` stores accepted transactions and has an index on `(ClienteId, Id DESC)` for recent-statement reads.
39
+
-`InsertTransacao` applies credit/debit balance updates and inserts the transaction only when the update succeeds.
40
+
-`GetSaldoClienteById` returns the current balance, limit, timestamp, and up to 10 latest transactions as JSONB.
41
+
42
+
The database command is tuned for benchmark throughput, not durability:
25
43
26
44
-`synchronous_commit=0` — no wait for WAL flush
27
45
-`fsync=0` — skip fsync on writes
28
46
-`full_page_writes=0` — skip full page writes
29
47
30
-
## Implementation Details
48
+
## Container Images
31
49
32
-
- Minimal single-file Rust API (~140 lines)
33
-
- Actix-web 4 HTTP framework with Tokio async runtime
34
-
- SQLx 0.8 async PostgreSQL driver
35
-
- Multi-stage Docker build for minimal container image size
50
+
The API Dockerfile builds with `rust:1.95` and runs on `debian:bookworm-slim` as a non-root `app` user. The release workflow publishes `ghcr.io/jonathanperis/rinha2-back-end-rust:latest` as the multi-arch image used by `prod/docker-compose.yml`.
-**Container smoke:**`docker compose -f ./docker-compose.yml up nginx --wait`, then `GET http://localhost:9999/healthz`
13
+
-**Purpose:** Catch Rust build failures, README/wiki drift, and compose health regressions before merging
12
14
13
15
### main-release.yml
14
16
15
-
-**Trigger:** Push to main branch
16
-
-**Steps:** Builds the release binary, builds and pushes a multi-platform Docker image (amd64/arm64) to GHCR, runs container healthcheck, then runs k6 load tests and uploads the stress test report as an artifact
17
-
-**Purpose:** Automated release of production-ready container images with load test validation
17
+
-**Trigger:** Push to `main` and manual dispatch
18
+
-**Build/release path:** build Rust release binary, push amd64 image as `latest`, push arm64 image as `latest-arm64`, merge both digests into the multi-arch `latest` manifest
19
+
-**Validation:** start `prod/docker-compose.yml`, poll `/healthz`, then run k6 in `MODE=prod`
20
+
-**Artifact:** upload `./prod/conf/stress-test/reports/stress-test-report.html` as `stress-test-report`
21
+
-**Purpose:** Publish production-ready container images and preserve the load-test report from the release run
18
22
19
23
### codeql.yml
20
24
21
-
-**Trigger:** Push to main, pull requests to main, weekly schedule (Mondays)
22
-
-**Steps:**Runs CodeQL static analysis for Rust with security-and-quality queries
25
+
-**Trigger:** Push to `main`, pull requests to `main`, and weekly schedule (`0 3 * * 1`)
26
+
-**Steps:**Set up stable Rust, initialize CodeQL for Rust with `security-and-quality` queries, build the release binary, perform analysis
23
27
-**Purpose:** Continuous security and code quality analysis
24
28
25
29
### deploy.yml
26
30
27
-
-**Trigger:** Push to main branch
28
-
-**Steps:** Deploys the `docs/` directory to GitHub Pages using the actions/deploy-pages workflow
29
-
-**Purpose:** Publish project documentation and stress test reports to GitHub Pages
31
+
-**Trigger:** Push to `main` and manual dispatch
32
+
-**Steps:** Calls the shared `jonathanperis/.github/.github/workflows/pages-docs-deploy.yml@main` reusable workflow with `package-manager: bun`
33
+
-**Purpose:** Build and publish the Astro docs site to GitHub Pages
34
+
35
+
## Local Documentation Drift Check
36
+
37
+
```bash
38
+
python3 scripts/check_docs_drift.py
39
+
```
40
+
41
+
The guard checks README/wiki claims against the current code and configuration: line count, Rust edition, Docker builder image, endpoints, workflow triggers, image tags, compose resources, and wiki navigation coverage. It also rejects known-stale phrases such as old line-count/version/performance claims.
In `docker-compose.yml`, k6 runs in `MODE=dev` and exports time-series data to InfluxDB/Grafana. The release workflow uses `prod/docker-compose.yml`, where k6 runs in `MODE=prod` and writes an HTML stress-test report artifact.
Copy file name to clipboardExpand all lines: docs/wiki/home.md
+11-9Lines changed: 11 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,23 +1,25 @@
1
1
# rinha2-back-end-rust
2
2
3
-
Rust/Actix-web 4 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).
3
+
Rust/Actix-web 4 implementation for the Rinha de Backend 2024/Q1 challenge. It manages a fictional bank API with transaction processing and balance statements under strict resource constraints (1.5 CPU, 550MB RAM total across the challenge containers).
4
4
5
5
## Wiki Pages
6
6
7
7
| Page | Description |
8
8
|------|-------------|
9
-
|[Challenge](#challenge)| What is Rinha de Backend 2024/Q1 |
0 commit comments