Performance Testing at Scale — k6, Valkey, and Caching Strategies
When your client is a national restaurant chain, "the menu API is slow" is not an abstract performance concern. It means thousands of customers staring at loading spinners during lunch rush. Revenue is literally on the line.
I led the performance testing initiative for this program, and the stack was k6 for load testing with Valkey as the caching layer.
Why k6
We evaluated several load testing tools. k6 won for three reasons: it is scriptable in JavaScript so our frontend-heavy team could write tests, it produces clean metrics without a heavy GUI, and it integrates well with our CI/CD pipeline. We needed tests that ran in automation, not tools that required someone to click buttons.
The Testing Strategy
We built test scenarios around real traffic patterns. Lunch rush meant 10x normal load concentrated in a 90-minute window. We simulated gradual ramp-up, sustained peak, and spike scenarios. The menu API was the primary target because it handled the most requests and had the most complex data joins.
The Valkey Layer
Valkey, the Redis fork, served as our caching layer. Menu data changes infrequently — maybe a few times per day — but gets read thousands of times per minute. We implemented a cache-aside pattern with TTLs aligned to the menu update schedule. Cache hit rates above 95% meant the database only handled cache misses and writes.
Results
After optimization, the menu API handled peak load with consistent sub-200ms response times. We identified and fixed three bottlenecks: an N+1 query in the menu item resolver, missing database indexes on category lookups, and suboptimal cache key design that caused unnecessary invalidations.
Performance testing is not glamorous work, but it is the difference between an app that works and an app that works under pressure.
←Back to all posts