- C# 59.9%
- JavaScript 14.1%
- TypeScript 12.1%
- HTML 6.9%
- PowerShell 3.9%
- Other 3.1%
| frontend | ||
| infra | ||
| services | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| ClassQueuing.sln | ||
| docker-compose.yml | ||
| README.md | ||
ClassQueuing
A mock high-traffic university course registration system demonstrating a Redis-backed virtual waiting room, atomic admission control, capacity-safe PostgreSQL registrations, observability, and k6 load testing.
Architecture
Browser -> Angular/nginx -> YARP Gateway -> Waiting Room -> Redis
|
+---------> Registration -> PgBouncer -> PostgreSQL
Prometheus <- application metrics + Redis/Postgres exporters -> Grafana
The waiting room uses Redis sorted sets for FIFO ordering and active-session expiry. A Lua admission operation removes expired sessions, calculates free slots, and admits the next students atomically. Registration reserves a course seat with a conditional PostgreSQL update inside the same transaction that inserts the unique student/course record. PgBouncer uses transaction pooling to queue database demand while keeping PostgreSQL backend connections bounded.
Run the demo
Requirements: Docker Desktop with Compose.
Build and start the stack once, while also caching the load-test runner image:
docker compose pull postgres redis pgbouncer redis-exporter postgres-exporter prometheus grafana k6-capacity k6-queue
docker compose build --pull gateway waiting-room course-registration frontend
docker compose up -d --no-build
Later starts reuse those images without rebuilding:
docker compose up -d --no-build
- Application: http://localhost:4200
- Gateway: http://localhost:5100
- Grafana dashboard: http://localhost:3000/d/classqueuing-overview/classqueuing-overview (
admin/admin) - Prometheus: http://localhost:9090
Configuration can be copied from .env.example. Defaults are 300 active students and five-minute registration sessions.
API
All user calls require X-Student-Id.
POST /api/waiting-room/queue/join
GET /api/waiting-room/queue/status
POST /api/waiting-room/queue/complete
GET /api/registration/courses
POST /api/registration/courses/{courseCode}/registrations
DELETE /api/registration/courses/{courseCode}/registrations
Protected /test/reset endpoints are enabled only when Testing:AdminToken is configured and exist to make the load tests repeatable.
Load tests
Run the full end-to-end scenario with 40,000 students arriving over five minutes:
docker compose run --rm --no-deps --pull never k6-queue
Each student joins the waiting room, waits for admission, registers for one of the three seeded courses (distributed evenly), and explicitly completes the session so the next student can enter. Setup resets the queue and gives each course 40,000 seats. The test passes only when all 40,000 students are admitted and completed and all 40,000 course registrations succeed. An optional SESSION_HOLD_SECONDS environment variable can add time inside the registration screen; its default is zero for this throughput scenario.
Run the concurrent race for exactly 100 CENG101 seats:
docker compose run --rm --no-deps --pull never k6-capacity
For a quick local queue check:
docker compose run --rm --no-deps --pull never -e TOTAL_USERS=1000 -e DURATION=30s -e PREALLOCATED_VUS=100 k6-queue
The k6 containers send their metrics to Prometheus remote write. The capacity script resets only its test course and queue, admits contenders, reports successful, conflict, server-error, and unexpected outcomes separately, and fails unless exactly 100 registrations succeed with no 5xx response. --no-deps --pull never guarantees that an already running stack is reused without rebuilding, recreating, or pulling its dependencies; Compose still creates one disposable k6 runner and --rm removes it afterward.
With the stack running, verify gateway routing, admission, seeded courses, registration, session release, and all Prometheus targets:
./tests/smoke.ps1
Development
The .NET solution targets .NET 9. Start PostgreSQL and Redis with Compose, then run the APIs on ports 5100–5102. npm start in frontend/ proxies /api to the gateway.
dotnet test ClassQueuing.sln
cd frontend && npm run build