The Problem We Were Actually Solving
Our treasure hunt engine was a Node.js microservice that handled player positions, loot drops, and real-time leaderboards over WebSockets. The hot path was a tight loop:
// Node 18, iojs build clients . forEach (( socket ) => { socket . write ( JSON . stringify ( positionUpdate )); });
Enter fullscreen mode
Exit fullscreen mode
Under load, clients.forEach ballooned into a heap-allocated array of 100 kB per player, causing 100 MB/s GC pressure and 200 ms p95 latency spikes. The profiler snapshot from 0x showed 42 % of CPU time inside v8s incremental-marking phase and 1.4 GB of heap allocated for a single request batch.
We could scale horizontally, but each container still burned 300 MB RSS and needed 30 % more CPU than the equivalent C++ service the infra team had benchmarked last quarter. Worse, every time the garbage collector kicked in, WebSocket heartbeats dropped and players saw a frozen map.
What We Tried First (And Why It Failed)
First idea: switch to Go.…