Skip to content

Persistence benchmark

The persistence benchmark creates a dedicated ephemeral PostgreSQL database, loads production-shaped synthetic rows, exercises the real store methods, and writes latency percentiles and query plans to a JSON report. It never connects to the normal development or comprehensive-test-course databases.

Terminal window
./scripts/benchmark-persistence.sh

The default profile creates 20,000 students, 20 exercises, 60,000 documents, 400,000 assessments, 440,000 assessment memberships, 400,000 grader assignments, and 100,000 manual-grading capabilities. The database and its volume are removed when the command finishes.

The report is written to backend/build/reports/persistence-benchmark.json. It contains p50, p95, and maximum latency for selective and course-wide reads, one bulk write, relation sizes, and EXPLAIN (ANALYZE, BUFFERS) output. The run fails if PostgreSQL does not use the expected selective indexes.

Set any of these variables before running the script:

Variable Default
BENCHMARK_STUDENTS 20,000
BENCHMARK_EXERCISES 20
BENCHMARK_DOCUMENTS_PER_STUDENT 3
BENCHMARK_DOCUMENT_BYTES 4,096
BENCHMARK_MANUAL_CAPABILITIES 100,000
BENCHMARK_WARMUPS 2
BENCHMARK_REPETITIONS 8
BENCHMARK_VERIFY_PLANS true
BENCHMARK_POSTGRES_PORT 55,433
BENCHMARK_REPORT backend/build/reports/persistence-benchmark.json

Keep plan verification enabled for performance runs. Small smoke profiles may disable it because PostgreSQL normally prefers sequential scans for tiny tables.

For example, a million-assessment stress run is:

Terminal window
BENCHMARK_STUDENTS=50000 BENCHMARK_EXERCISES=20 \
./scripts/benchmark-persistence.sh

Set KEEP_BENCHMARK_DB=1 to retain the isolated database after the run for manual EXPLAIN inspection. Remove it afterward with:

Terminal window
docker compose -f docker-compose.benchmark.yml \
-p hanami-persistence-benchmark down -v