← Back to blog
Solution 4 min read 炬鲸团队

Game Server Observability: Tracking CCU, Latency, and Stutter

Game backends need different observability than web apps: CCU, frame latency, stutter, and disconnect rate. This post shows how to trace player-reported lag and set alerting for launch spikes.

Game Servers Need Different Metrics Than Web Apps

A web service can get by with QPS, latency, and error rate. A game server also has to track concurrent users (CCU), frame-sync latency, disconnect rate, stutter rate, and load distribution across shards. These map directly to player experience: a sudden CCU dip usually means a shard crashed; a frame-latency spike usually means one room's gateway is drowning.

Build metrics in three layers: infrastructure (CPU/memory/disk/network), service (latency and error rate for login, matchmaking, combat, and payment), and experience (CCU, frame latency, disconnect rate, stutter reports). The experience layer is closest to the player and the easiest to overlook — a service can look perfectly healthy on CPU graphs while players are quietly leaving.

Finding "Stutter" with Traces

A player complains that team fights lag, but your server CPU looks fine. Stutter often hides in a single call on the path: the settlement service runs a slow SQL, or matchmaking times out calling a third-party auth API. Tracing gets you there:

  1. Search traces by the match ID or openid and pull every call in that match;
  2. Sort by duration, find the slowest span, and open its downstream calls and SQL;
  3. Aggregate similar slow spans to see if they cluster on a specific room, shard, or time window.

One classic root cause: the gateway has no backpressure on its per-connection write buffer, so one slow client stalls an entire room. You can only catch that by reading traces and log context together — the CPU graph never shows it, but the trace shows one connection holding the lock while everyone else queues behind it. Games are also notoriously chatty on the wire: hundreds of small messages per second per player means a single hot path can produce a firehose of spans, so sampling strategy matters more than in a CRUD app.

Alerting for Launch Peaks and Betas

Game traffic spikes are extremely steep: an open, a patch, or an event can push CCU from thousands to hundreds of thousands in minutes. Alerting has to adapt:

  • Day-to-day: fixed thresholds, e.g., a shard's CPU > 85% for 5 minutes;
  • Launch/event: switch to dynamic thresholds plus period-over-period comparison, because fixed thresholds get tripped instantly by the peak and spam you;
  • Beta/load test: open a separate silence window so test data doesn't pollute production alerts.

Also add experience-level alerts like "disconnect-rate surge" with a very low bar (e.g., disconnect rate up 3× over 5 minutes), because disconnects are a direct churn signal and deserve priority over a CPU alert. A player who disconnects is a player you might not get back.

Rollout Checklist: Where to Start

If you're starting now, go in this order:

  1. Ship logs and metrics first; build the CCU, disconnect-rate, and per-shard load dashboards;
  2. Add tracing to the four core paths — login, matchmaking, combat, payment — and make sure a match ID can pull them up;
  3. Then configure alerting: fixed thresholds day-to-day, dynamic during events, plus the disconnect-surge alert;
  4. Review after every major version or event: aggregate the top slow calls from traces and queue them for the next iteration.

Get "we can find the problem" working before you chase "there's no problem." The first is a tool you buy; the second is a practice you earn.

One nuance specific to games: treat client-side and server-side telemetry as a single picture. A "stutter" a player reports may originate in the client's rendering loop, not your backend — so correlate server traces with client frame-time events wherever you can. Even a simple shared session ID that ties a client report to the server trace saves hours of back-and-forth between support and backend teams. And remember that game workloads are highly stateful: a restart costs you every in-progress match, so observability that helps you avoid a restart pays for itself almost immediately. That is the difference between fixing a room's gateway and apologizing to ten thousand players.