o
odinpkg.dev
packages / library / ragnarok_server

ragnarok_server

2b1e2f0library

odin http 1 server

No license · updated 4 months ago

ragnarok_server

Testing

Memory Leak Tests

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.

Run all tests

odin test ragnarok_http -all-packages

Run a single test by name

odin test ragnarok_http -all-packages -define:ODIN_TEST_NAMES=ragnarok_http.test_request_arena_no_leak

Run multiple specific tests

Comma-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_leak

Control thread count

odin test ragnarok_http -all-packages -define:ODIN_TEST_THREADS=1

What the tests cover

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

How it works

Each test follows the same pattern:

  1. Create a mem.Tracking_Allocator over the default heap allocator
  2. Run the code under test, passing the tracking allocator
  3. Clean up (e.g. request_arena_destroy)
  4. Assert that the tracking allocator's allocation_map is empty (no leaks) and bad_free_array is empty (no invalid frees)

Any leaked allocation is printed with its size and source location for easy debugging.

Benchmarks

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.

Build and run benchmarks

odin build benchmarks -o:speed
.\benchmarks.exe

Or in one step:

odin run benchmarks -o:speed

The -o:speed flag enables compiler optimizations for realistic performance numbers.

HTML report

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

What the benchmarks measure

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

How it works

Each benchmark:

  1. Creates a mem.Tracking_Allocator to count allocations and bytes
  2. Runs the workload for a fixed iteration count (50K–1M depending on cost)
  3. Records per-iteration min/max/avg nanoseconds via time.tick_now()
  4. Computes derived metrics: ops/sec, allocs/op, bytes/op, allocation rate, throughput MB/s
  5. Request-oriented benchmarks also track p50, p99 latency and peak req/s (windowed)
  6. Results are printed to the console and aggregated into the HTML report

Real-World HTTP Benchmarking

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

Start the server

odin build . -o:speed -out:ragnarok.exe
.\ragnarok.exe

Benchmark with bombardier (Windows)

Install: 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/json

The -l flag enables latency percentiles (p50, p75, p99, p99.9).

Benchmark with wrk (Linux / WSL)

# Plaintext
wrk -t4 -c128 -d10s http://localhost:8080/plaintext

# JSON
wrk -t4 -c128 -d10s http://localhost:8080/json

Tuning parameters

Adjust 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

Docker Benchmarking

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.

Build the Docker image

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 .

Run the server

docker run --privileged -p 8080:8080 ragnarok

Note: --privileged is required because the server uses io_uring for async I/O on Linux.

Run wrk benchmarks (with HTML report)

docker run --rm --privileged -v "${PWD}/reports:/app/reports" ragnarok /app/scripts/wrk-bench.sh

This will:

  1. Start the Ragnarok server inside the container
  2. Run wrk --latency against /plaintext and /json
  3. Test 6 concurrency levels: 1, 8, 32, 64, 128, 256 connections
  4. Generate reports/wrk_report.html with 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

Customize the benchmark

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.sh

Run the script on the host (server already running)

If you have wrk installed locally (Linux/WSL):

HOST=localhost PORT=8080 REPORT_DIR=./reports bash scripts/wrk-bench.sh