The project includes a comprehensive memory leak test suite in ragnarok_http/memory_leak_tests.odin. Tests use Odin's built-in mem.Tracking_Allocator to wrap every allocation and assert zero leaks after cleanup.
odin test ragnarok_http -all-packagesodin test ragnarok_http -all-packages -define:ODIN_TEST_NAMES=ragnarok_http.test_request_arena_no_leakComma-separate the names:
odin test ragnarok_http -all-packages -define:ODIN_TEST_NAMES=ragnarok_http.test_parse_request_no_leak,ragnarok_http.test_response_serialize_no_leakodin test ragnarok_http -all-packages -define:ODIN_TEST_THREADS=1| Area | Tests |
|---|---|
| Request_Arena lifecycle | init/destroy, reset/reuse, double-destroy safety, stress (200 allocs), isolation across 8 concurrent arenas |
| Connection_Pool | init/destroy, acquire/release cycles, acquire with arena lifecycle, burst (100 connections), double-release safety |
| parse_request | GET with query params, POST with content-length, malformed-input failure paths |
| response_serialize | normal response, many headers, empty 204 response |
| Handler cycle | health, not-found (404), method-not-allowed (405), full parse→route→respond→serialize |
| Repeated cycle | 50 full request–response cycles with arena create/destroy |
| format_error_response | stack-buffer formatting (zero heap allocs) |
| build_file_path | path construction with allocator cleanup |
Each test follows the same pattern:
- Create a
mem.Tracking_Allocatorover the default heap allocator - Run the code under test, passing the tracking allocator
- Clean up (e.g.
request_arena_destroy) - Assert that the tracking allocator's
allocation_mapis empty (no leaks) andbad_free_arrayis empty (no invalid frees)
Any leaked allocation is printed with its size and source location for easy debugging.
The benchmark suite in benchmarks/benchmarks.odin measures memory, CPU, allocation rates, requests per second, and concurrent request throughput. It outputs both console results and an HTML report.
odin build benchmarks -o:speed
.\benchmarks.exeOr in one step:
odin run benchmarks -o:speedThe -o:speed flag enables compiler optimizations for realistic performance numbers.
After running, a benchmark_report.html file is generated in the project root. Open it in a browser to view an interactive dashboard with:
- Summary cards (total ops, req/sec, fastest avg, heap allocations)
- Per-category tables with avg/min/max ns, ops/sec, allocs/op, bytes/op, MB/s
- Relative performance bar charts
| Category | Benchmarks |
|---|---|
| Memory | Arena init+destroy, arena with 20 allocations + reset, arena reset+reuse (keep-alive pattern), pool acquire+release |
| Allocation Rate | 50×64B small allocations, mixed-size allocations (32B–1024B) |
| CPU / Parsing | Parse GET request (5 headers), parse POST request, parse request with 16 headers |
| CPU / Routing | Router match (last of 10 routes), router miss (full scan) |
| CPU / Serialization | Serialize small response, serialize 16KB response, format_error_response (stack-only, zero heap) |
| Requests/sec | Full request cycle: parse → route → handle → serialize → arena destroy (with p99, peak req/s) |
| Concurrent Requests | Simulated connection lifecycle with 32-slot pool, pool burst (64 acquire+release) (with p99, peak req/s) |
| Throughput | 1, 8, 32, 64, 128 concurrent connections (batched), keep-alive pipeline (arena reuse). Reports avg req/s, peak req/s, p50, p99 latency |
Each benchmark:
- Creates a
mem.Tracking_Allocatorto count allocations and bytes - Runs the workload for a fixed iteration count (50K–1M depending on cost)
- Records per-iteration min/max/avg nanoseconds via
time.tick_now() - Computes derived metrics: ops/sec, allocs/op, bytes/op, allocation rate, throughput MB/s
- Request-oriented benchmarks also track p50, p99 latency and peak req/s (windowed)
- Results are printed to the console and aggregated into the HTML report
The server includes TechEmpower-style routes for benchmarking with real HTTP load generators:
| Route | Response | Content-Type |
|---|---|---|
GET /plaintext |
Hello, World! |
text/plain; charset=UTF-8 |
GET /json |
{"message":"Hello, World!"} |
application/json; charset=UTF-8 |
odin build . -o:speed -out:ragnarok.exe
.\ragnarok.exeInstall: go install github.com/codesenberg/bombardier@latest or scoop install bombardier
# Plaintext
bombardier -c 128 -d 10s -l http://localhost:8080/plaintext
# JSON
bombardier -c 128 -d 10s -l http://localhost:8080/jsonThe -l flag enables latency percentiles (p50, p75, p99, p99.9).
# Plaintext
wrk -t4 -c128 -d10s http://localhost:8080/plaintext
# JSON
wrk -t4 -c128 -d10s http://localhost:8080/jsonAdjust concurrency (-c) and threads (-t) to match your hardware:
| Flag | Meaning | Suggestion |
|---|---|---|
-c 128 |
Concurrent connections | Start here, scale to 256/512 |
-t 4 |
wrk threads | Match your CPU core count |
-d 10s |
Duration | 10s minimum for stable results |
The project includes a Dockerfile that builds the server on Linux and bundles wrk for automated benchmarking. A single command builds, starts the server, runs wrk at multiple concurrency levels against both /plaintext and /json, and generates an HTML report.
docker build -t ragnarok .By default the Dockerfile downloads the dev-2026-02 Odin release. To use a different version:
docker build -t ragnarok \
--build-arg ODIN_RELEASE=dev-2026-01 \
--build-arg ODIN_URL=https://github.com/odin-lang/Odin/releases/download/dev-2026-01/odin-linux-amd64-dev-2026-01.tar.gz .docker run --privileged -p 8080:8080 ragnarokNote:
--privilegedis required because the server usesio_uringfor async I/O on Linux.
docker run --rm --privileged -v "${PWD}/reports:/app/reports" ragnarok /app/scripts/wrk-bench.shThis will:
- Start the Ragnarok server inside the container
- Run
wrk --latencyagainst/plaintextand/json - Test 6 concurrency levels: 1, 8, 32, 64, 128, 256 connections
- Generate
reports/wrk_report.htmlwith a dark-themed dashboard
The HTML report includes:
- Summary cards (peak req/s per endpoint, overall peak)
- Per-endpoint tables with req/s, avg latency, p50/p75/p90/p99, max latency, transfer/sec
- Relative performance bars
Environment variables:
| Variable | Default | Description |
|---|---|---|
DURATION |
10s |
wrk test duration per run |
WRK_THREADS |
4 |
Number of wrk threads |
HOST |
localhost |
Server hostname |
PORT |
8080 |
Server port |
REPORT_DIR |
/app/reports |
Output directory for HTML report |
Example with custom settings:
docker run --rm \
-e DURATION=30s \
-e WRK_THREADS=8 \
-v "${PWD}/reports:/app/reports" \
ragnarok /app/scripts/wrk-bench.shIf you have wrk installed locally (Linux/WSL):
HOST=localhost PORT=8080 REPORT_DIR=./reports bash scripts/wrk-bench.sh