Load test for /prices at 5k rps (k6)#71
Conversation
…ntion and query API
…tHub Actions integration
|
@githoboman Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Miracle656
left a comment
There was a problem hiding this comment.
The k6 load test (tests/load/prices.k6.js) and the load.yml workflow are exactly what #62 needs — the constant-arrival-rate executor and the p(95)<80 + http_req_failed < 1% thresholds are the right design.
The blocker: this branch is stacked on your price-history-snapshots work (#70), so it currently also includes all the snapshot/history/retention files. #70 has now been merged to main, so please rebase on the latest main and drop everything except the load-test files (tests/load/prices.k6.js + .github/workflows/load.yml). Once it's just the load test, I'll merge it.
Here's what I built and why.
Closes #62
Files
tests/load/prices.k6.js (new) — the k6 load test
constant-arrival-rate executor — drives load in requests/sec (open model), so it actually holds the target RPS regardless of how fast the server responds. A closed VU model would silently drop below 5k RPS if latency rose, masking failures.
Threshold http_req_duration: ['p(95)<80'] — the acceptance criterion, enforced by k6's exit code (non-zero on breach → CI fails).
Also fails on errors / http_req_failed rate ≥ 1%, so a flood of 429s/401s can't masquerade as a passing fast run.
Fully env-configurable: BASE_URL, RATE (default 5000), DURATION (default 1m), API_KEY, PAIRS (default XLM/USDC, the docker-compose watched pair — findPair normalizes native→XLM).
.github/workflows/load.yml (new) — scheduled CI run
Daily at 04:00 UTC (off-peak) + manual workflow_dispatch with rate/duration inputs.
Spins up postgres + redis service containers, builds, applies schema, boots the server, warms the price cache (so the run measures the production hot path bots hit, not a cold miss), then runs k6 via the official Grafana actions.
src/index.ts (edited) — made the rate-limiter's IP fallback configurable via RATE_LIMIT_IP_MAX (default unchanged at 100). Required because a 5k-RPS flood from one IP would otherwise hit the hardcoded 100/min cap and 429 everything — the test would measure the limiter, not the endpoint. The workflow sets it high and runs with REQUIRE_API_KEY=false to isolate endpoint latency.
Verification
node --check on the k6 script: passes.
tsc --noEmit: my edit adds no new errors. The two webhookDispatcher.ts errors are pre-existing (confirmed by stashing my changes — they're on the clean a9923d5 baseline), so the existing CI is already red on them; that's out of scope here but worth flagging.