# order-cache-lab **Repository Path**: MuMuNan/order-cache-lab ## Basic Information - **Project Name**: order-cache-lab - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-19 - **Last Updated**: 2026-06-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Order Cache Lab A Go + MySQL order system that gets benchmarked across cache strategies. The point isn't a complex business system — it's having a simple, repeatable harness to measure the impact of each cache layer as it's added. See [plan.md](./plan.md) for the full plan and [CLAUDE.md](./CLAUDE.md) for project rules. ## Versions | Tag | Branch | Strategy | |-----|--------|----------| | `v0` | `feature/v0_mysql_only` | MySQL only (baseline) | | `v1` | `feature/v1_product_cache_aside` | Redis Cache Aside for product | | `v2` | `feature/v2_ttl_random` | TTL + random TTL | | `v3` | `feature/v3_null_cache` | Null cache (penetration) | | `v4` | `feature/v4_singleflight` | Singleflight (stampede) | | `v5` | `feature/v5_hot_product_cache` | Hot product list cache | | `v6` | `feature/v6_order_cache` | Order read cache | | `v7` | `feature/v7_local_cache` | Local in-process cache + Redis | Each version was developed on its own `feature/v{n}_*` branch and merged into `master` with a `v{n}` tag. Branches are preserved so you can `git checkout feature/v{n}_*` to see the work-in-progress, or `git checkout v{n}` to view the merged baseline. ## Cross-version QPS summary (product_read, hot_key, penetration) Captured at `10s x 10 VUs` on a single local machine — see `benchmark/results/compare.md` for the full matrix. | Version | product_read | hot_key | penetration | |---------|--------------|---------|-------------| | v0 | 6,844 | 6,783 | 6,403 | | v1 | 14,492 | 16,254 | 5,774 | | v2 | 13,449 | 15,609 | 5,701 | | v3 | 14,426 | 15,741 | 16,896 | | v4 | 14,044 | 15,116 | 16,075 | | v5 | 14,273 | 15,970 | 15,674 | | v6 | 13,541 | 15,091 | 16,040 | | v7 | 26,316 | 31,758 | 30,868 | ## Stack * Go 1.22+, Gin, GORM, MySQL 8, Redis 7 * Docker Compose for MySQL/Redis * k6 for load tests (works via the `grafana/k6` Docker image if k6 is not installed locally) ## Quickstart ```bash # 1. start MySQL + Redis docker compose -f deploy/docker-compose.yml up -d # 2. start the server (auto-migrates schema on first run) go run cmd/server/main.go # 3. seed test data: 100 users, 10000 products (20 hot) bash scripts/seed_data.sh # 4. unit + repo + service tests go test ./... # 5. one-shot benchmark (V0 baseline) bash benchmark/run.sh v0_mysql_only # 6. cross-version comparison python3 benchmark/compare.py benchmark/results ``` ## Configuration All configuration is via environment variables: | Var | Default | |-----|---------| | `MYSQL_HOST` | localhost | | `MYSQL_PORT` | 3306 | | `MYSQL_USER` | root | | `MYSQL_PASSWORD` | root | | `MYSQL_DATABASE` | order_cache_lab | | `REDIS_HOST` | localhost | | `REDIS_PORT` | 6379 | | `SERVER_PORT` | 8080 | ## API ``` POST /products create product GET /products/:id read product PUT /products/:id update product GET /products/hot hot product list POST /orders create order (transactional stock deduction) GET /orders/:id read order GET /orders?user_id=X list user's orders POST /orders/:id/pay pay order POST /orders/:id/cancel cancel order GET /debug/stats current counters (db queries, cache hit/miss, errors) POST /debug/stats/reset reset counters (called by benchmark runner) GET /healthz liveness ``` `/debug/stats` returns: ```json { "db_query_count": 0, "db_query_product_count": 0, "db_query_order_count": 0, "cache_hit": 0, "cache_miss": 0, "cache_hit_rate": 0, "order_create_count": 0, "error_count": 0 } ``` ## Benchmarks `benchmark/run.sh ` runs all six k6 scenarios sequentially: | Scenario | What | |----------|------| | `product_read` | 90% hot id (1-100), 10% cold id (101-10000) | | `order_read` | random reads of `/orders/:id` | | `create_order` | random user + random product, qty=1 | | `mixed` | 80% product read / 10% order read / 10% create | | `penetration` | non-existent ids (100M+) — for null-cache test | | `hot_key` | every request hits product id 1 — for stampede test | Tunables: ```bash BASE_URL=http://localhost:8080 \ VUS=100 \ DURATION=60s \ bash benchmark/run.sh v1_cache_aside ``` Each run produces `benchmark/results///`: ``` .raw.json # k6 line-delimited samples .summary.json # k6 summary export .app_stats_before.json # /debug/stats before run .app_stats_after.json # /debug/stats after run summary.csv # all scenarios in one CSV summary.json # structured summary report.md # human-readable per-scenario table ``` `benchmark/compare.py benchmark/results` walks every version's latest run and writes `compare.csv` and `compare.md`. ### Tests ```bash # unit + repo + service tests (uses order_cache_lab_test DB) go test ./... # benchmark collector tests (Python) python3 benchmark/collect_metrics_test.py ``` Repository and service tests skip if MySQL is not reachable, so `go test ./...` is safe to run without the docker stack up.