Reference: Backpressure and Degradation
Last verified 2026-09-05 against engine v1.0.232.
Overload is the runtime's answer to being asked for more than a process can afford: a burst of author state, a client that will not drain its socket, more rooms than one instance should host. The response is server-owned graceful degradation, a fixed order in which cheap-to-lose work is shed first and a room is refused or stopped last. This page enumerates the overload surfaces, the invariants and the conformance test that holds each, the constants with the receipt that pins them, the typed error codes, the proof surfaces, and the limits (which rungs are wired into a live actuator and which are declared but inert). For the model behind these values, read the Overload and Degradation Model; this page states the values, that page states the why. For the wider runtime this sits inside, read the Netcode Contract reference.
Source of truth
This subsystem has no single generated file. Its source of truth is the runtime code in server/src/modules/runtime plus the fixture-hash test server/tests/runtimeNetcodeContractFixtureHash.test.ts, which pins the overload constants by value. The typed error codes a shed effect surfaces under are the exception: they are generated into the runtime error registry and this page links down to it.
The mechanisms live in backpressure.ts (the summary and both ladders), streamBudget.ts (the ingress governor), shard/shardCapacityPolicy.ts (the capacity floor), runtimePolicies.ts (the slow-consumer, load-test, and state-stream policy constants), replicationTransport.ts (the slow-consumer machine), and roomActor.ts (the live ingress admission path), all under server/src/modules/runtime.
Surfaces
| Kind | Surface | Symbol and file |
|---|---|---|
| function | Backpressure summary | summarizeRuntimeBackpressure in backpressure.ts |
| enum | Wire classification | backpressureState (none, watch, throttling, shedding) in packages/protocol/src/index.ts |
| constant | Per-room ladder | RUNTIME_LOAD_DEGRADATION_ORDER in backpressure.ts |
| constant | Per-shard ladder | RUNTIME_SHARD_LOAD_DEGRADATION_ORDER in backpressure.ts |
| function | Per-shard verdict | evaluateShardDegradation in backpressure.ts |
| class | State-stream governor | RuntimeStateStreamBudget in streamBudget.ts |
| function | Slow-consumer stage | slowConsumerStage in replicationTransport.ts |
| function | Placement verdict | evaluateRoomPlacement in shard/shardCapacityPolicy.ts |
| function | Placement enforcement | assertRoomPlacementWithinBudget in shard/shardCapacityPolicy.ts |
| constant | Capacity caps | RUNTIME_SHARD_CAPACITY_POLICY in shard/shardCapacityPolicy.ts |
| constant | Durability knee | RUNTIME_LOAD_TEST_POLICY in runtimePolicies.ts |
| constant | Slow-consumer bytes | RUNTIME_SLOW_CONSUMER_POLICY in runtimePolicies.ts |
Backpressure summary
summarizeRuntimeBackpressure classifies a room's replication health from two independent signals and reduces them to one backpressureState. The first signal is acknowledgement lag: for each connected-or-degraded connection, how far the server's latest sequence has run ahead of what the client last acknowledged.
The summary reads the maximum and the p95 of that lag and trips the highest of the three thresholds ACK_LAG_WATCH, ACK_LAG_THROTTLING, and ACK_LAG_SHEDDING, or escalates from the count of connections already flagged degraded (greater than zero is throttling; greater than max(4, active / 2) is shedding). The second signal is gateway pressure, reconstructed from each connection's metadata.gatewayPressure, which the routes layer writes on an envelope-too-large or command-over-budget rejection. The two are combined by rank, worst wins:
The classification is observational: it labels a room so operators and clients can read the strain, and it drives no shed on its own path. The shedding is performed by the mechanisms below.
The per-room ladder
RUNTIME_LOAD_DEGRADATION_ORDER is the fixed per-room shed order. Its rungs, by increasing severity, are downsample_or_drop_state_streams, drop_low_priority_replication, reject_room_commands, evict_slow_consumers, and mark_room_unhealthy. The array is a declaration, not a dispatcher: it performs no action itself, and each rung is realized by a separate module.
| Rung action | Realized by |
|---|---|
downsample_or_drop_state_streams | RuntimeStateStreamBudget in streamBudget.ts |
drop_low_priority_replication | slowConsumerStage throttle in replicationTransport.ts |
reject_room_commands | the room-scope command shed in routes.ts (runtime_room_pressure_shedding) |
evict_slow_consumers | slowConsumerStage evict in replicationTransport.ts |
mark_room_unhealthy | the tick fuse failureLimitForRoom in tickScheduler.ts |
The per-shard ladder
RUNTIME_SHARD_LOAD_DEGRADATION_ORDER is meant to compose above the per-room ladder when rooms run on worker-thread shards. Its rungs, by increasing severity, are thin_snapshots, reduce_snapshot_rate, pause_non_essential_lanes, refuse_joins, and migrate_or_close; each carries a stable action, a typed error code, and an operator-facing trigger. The selector evaluateShardDegradation is a pure function of a shard's signals { tickP99Ms, frameBudgetMs, ringOccupancyRatio, roomCount, roomCapacity } that returns the single highest active rung, walked from calm upward. The thresholds are budget-relative, so the ladder holds at any tick rate:
The two top rungs set refusesJoins (the verdict's order at or above four), and reaching the room cap alone (roomCount at or above roomCapacity) refuses joins even with a calm tick loop. The literals in that piecewise definition are the source of truth in evaluateShardDegradation itself; the ladder's conformance test proves the rung order and the typed code each rung emits, not that any interior threshold is drift-protected (see Proof surfaces).
The state-stream governor
Author-driven state streams pass RuntimeStateStreamBudget before admission. A sample runs a chain of sliding one-second windows over four scopes in order: per-stream rate, per-stream bytes, per-player aggregate bytes, and per-room aggregate bytes. The first scope it would exceed rejects it with a typed reason (rate_exceeded, byte_budget_exceeded, player_byte_budget_exceeded, or room_byte_budget_exceeded), a retryAfterMs, and a specific hint: a rate rejection hints drop_next, a byte or aggregate rejection hints downsample. An accepted sample whose utilization has reached DEGRADE_RATIO still carries a downsample hint, so a well-behaved client backs off before it starts losing samples. Ahead of the budget, on the same ingest path (admitStreamSampleInMemory in roomActor.ts), a per-player distinct-stream cap, enforcePlayerStreamStateLimit, bounds how many streams one player may open so the rate and byte windows never police an unbounded fan-out.
The window length WINDOW_MS and the DEGRADE_RATIO are the two governor constants exercised by netcode-state-stream-phase9.test.ts. The four scope budgets themselves are carried by RUNTIME_STATE_STREAM_POLICY in runtimePolicies.ts and surfaced in the Netcode Contract reference; no test value-pins them, so they are recorded in the dossier's Unverified list rather than stated here.
The slow-consumer machine
Replication is unreliable by design, but a client that stops draining its socket still costs the server memory. Per outbound socket, slowConsumerStage in replicationTransport.ts maps the socket's bufferedAmount through the byte ladder in RUNTIME_SLOW_CONSUMER_POLICY to a watch, throttle, or evict stage, and the stage is latched (via slowConsumerStageRank) so it can only rise. Under throttle, only a low-priority (replication) frame is dropped, with reason backpressure_throttle; higher-priority frames still go out. Under evict, the subscriber is detached with WebSocket close code 1008. These two stages are the live realizations of the per-room ladder's drop_low_priority_replication and evict_slow_consumers rungs.
The capacity floor
Before a room joins the tick loop, evaluateRoomPlacement checks the placement against RUNTIME_SHARD_CAPACITY_POLICY and returns admit or a typed refusal naming the first breached scope; assertRoomPlacementWithinBudget throws that refusal (runtime_shard_over_budget, category capacity) so an over-budget room never starts ticking. Instance scopes are always checked; shard scopes are checked only when the placement is sharded. The live enforcement site is assertRoomPlacementWithinInstanceBudget in service.ts, reached from ensureLocalRoomTickAuthority before tickScheduler.ensureRoom, and it places with the unsharded flag, so in production only the per-instance scopes are consulted. The sharded site placeRoom in shard/shardHost.ts places with the sharded flag but runs only under the test harness, since ShardHost is instantiated only in tests.
The durability knee RUNTIME_LOAD_TEST_POLICY records the room and player counts a single loop was validated to sustain. It is a load-test guardrail, consumed only by loadTestRunner.ts (which throws when a plan exceeds it), not a production admission gate, and its room knee sits below the enforced per-instance room cap.
Typed error codes
The client-visible codes a shed effect surfaces under are generated into the runtime error registry.
| Code | Where it fires | Live |
|---|---|---|
runtime_room_pressure_shedding | room-scope command shed in routes.ts (realizes per-room rung three) | yes |
backpressure_throttle | slow-consumer throttle drop reason in replicationTransport.ts | yes |
runtime_shard_over_budget | over-budget placement refusal in shard/shardCapacityPolicy.ts; also shard rung four | placement path yes; shard rung inert |
runtime_shard_shedding | shard rungs one to three in backpressure.ts | inert (no actuator) |
game_server_restarting | room-lifecycle not-ready path in runtimeGatewayErrors.ts; also shard rung five | lifecycle path yes; shard rung inert |
Numbers
Each value names its constant or function (the source of truth) and the test or check that pins it. Provisional values carry the code's own provisional qualifier and must never be quoted as a tuned service level.
| Value | Constant or function | Receipt |
|---|---|---|
| ack-lag thresholds 8 / 32 / 128 (sequence) | ACK_LAG_WATCH, ACK_LAG_THROTTLING, ACK_LAG_SHEDDING in backpressure.ts | runtimeNetcodeContractFixtureHash.test.ts |
| slow-consumer bytes 1 / 4 / 16 MiB | RUNTIME_SLOW_CONSUMER_POLICY in runtimePolicies.ts | runtimeNetcodeContractFixtureHash.test.ts |
| capacity caps 200 / 2000 per instance; 32 / 320 per shard (provisional) | RUNTIME_SHARD_CAPACITY_POLICY in shard/shardCapacityPolicy.ts | runtimeNetcodeContractFixtureHash.test.ts |
| durability knee 128 rooms / 1024 players | RUNTIME_LOAD_TEST_POLICY in runtimePolicies.ts | runtimeNetcodeContractFixtureHash.test.ts (constant pinned; gates only load-test plans) |
| state-stream degrade ratio 0.8 | DEGRADE_RATIO in streamBudget.ts | netcode-state-stream-phase9.test.ts (behavioral) |
| state-stream window 1000 ms | WINDOW_MS in streamBudget.ts | netcode-state-stream-phase9.test.ts (behavioral) |
| shard tick ratios 0.8 / 1.0 / 1.25 / 1.5 / 2.0 (rungs one to five) | evaluateShardDegradation in backpressure.ts | shardPlacementCapacityLadder.test.ts (order and typed code; interior literals not drift-gated) |
| shard ring ratios 0.5 / 0.7 / 0.85 / 0.95 / 1.0 (rungs one to five) | evaluateShardDegradation in backpressure.ts | shardPlacementCapacityLadder.test.ts (order and typed code; exact equality asserted only at ring at or above 1.0) |
| tick fuse default 3 | RUNTIME_DEFAULT_RUNTIME_BUDGET.tickFailureMaxConsecutive in runtimePolicies.ts | runtimeRuntimePolicies.test.ts |
| Free-tier capacity 10 players / 20 Hz / 1 room (provisional; higher tiers pending) | docs/spec/RUNTIME_CAPACITY_TIER_MATRIX.md | run 2026-06-02T21-49-rssfix-final12h |
Invariants
| Invariant | Proved by |
|---|---|
| The per-room shed order equals the pinned sequence downsample-streams, drop-low-priority-replication, reject-commands, evict-slow-consumers, mark-unhealthy. | netcode-load-hardening.test.ts |
| The per-shard ladder is ordered, typed, and severity-dominant: rising tick-p99 walks it emitting the correct action and code, ring occupancy drives it independently, the room cap alone refuses joins, the highest active rung wins, and identical signals give an identical verdict. | shardPlacementCapacityLadder.test.ts |
The state-stream scopes are isolated and deterministically hinted: each rejection surfaces its own reason, a rate rejection hints drop_next and byte and aggregate rejections hint downsample, an accept at or above the degrade ratio still hints downsample, and command and stream budgets stay isolated under floods. | netcode-state-stream-phase9.test.ts |
Placement refuses before joining the tick loop: an over-budget placement throws runtime_shard_over_budget (category capacity) and the room never joins, while an idempotent re-ensure excludes the room itself from the census. | runtimeLocalTickAuthorityCapacity.test.ts |
| The overload constants are drift-gated by value (ack thresholds, slow-consumer bytes, shard caps with the provisional flag, and the durability knee) and the netcode contract emits a stable fixture hash. | runtimeNetcodeContractFixtureHash.test.ts |
| The tick fuse consecutive-failure limit defaults to three, takes a per-room override, and a run that reaches it is stopped. | runtimeRuntimePolicies.test.ts and runtimeTickScheduler.test.ts |
Limits and non-features
- The per-shard degradation ladder is not wired into a production actuator.
evaluateShardDegradationhas noserver/srccaller, and the shard host collects load signals throughrequestGovernorinshard/shardHost.tsbut nothing evaluates them into a live shed action. - The live tick-authority path is unsharded.
assertRoomPlacementWithinInstanceBudgetinservice.tsplaces with the unsharded flag, so the per-shard scopes are never consulted in production, andShardHost(the only shardedplaceRoomcaller) is instantiated only in tests. - The capacity caps are provisional placeholders, not measured service levels.
provisional: trueonRUNTIME_SHARD_CAPACITY_POLICYis load-bearing; at the generous defaults admission does not refuse in normal production. - The per-room ladder is a declaration, not a dispatcher.
RUNTIME_LOAD_DEGRADATION_ORDERperforms no action; its rungs are realized by separate modules. - The backpressure summary is observational.
summarizeRuntimeBackpressureclassifies a room and drives no shed on its own path. - The durability knee gates only load-test plans.
RUNTIME_LOAD_TEST_POLICYis consumed byloadTestRunner.ts, not by live admission, and its room knee sits below the enforced per-instance cap, so production admission can exceed the durability-validated knee. - Only the Free capacity tier is benchmark-validated. The Pro, Premium, and Enterprise rows of
RUNTIME_CAPACITY_TIER_MATRIX.mdremain evidence-gated.
Proof surfaces
The guarantees are held by tests and static gates, not by prose.
- Per-room order:
server/tests/netcode-load-hardening.test.tspins theRUNTIME_LOAD_DEGRADATION_ORDERaction sequence. - Per-shard ladder:
server/tests/shard/shardPlacementCapacityLadder.test.tsdrivesevaluateShardDegradationup a bracketing ramp and asserts the rung order and the typed code each rung emits. It brackets the interior tick and ring thresholds rather than pinning them exactly (it asserts exact equality only at ring at or above 1.0 and atroomCountat or aboveroomCapacity), so a downward drift of an interior literal would still pass. The function is the source of truth for those literals; the test proves order and code, not exact-value protection. - State-stream scopes:
server/tests/netcode-state-stream-phase9.test.tsproves each scope's isolated reason and hint, the accept-with-downsamplebehavior at the degrade ratio, and command-versus-stream budget isolation under floods. - Placement refusal:
server/tests/runtimeLocalTickAuthorityCapacity.test.tsproves an over-budget room never joins the tick loop and the refusal carries category capacity. - Constant drift gate:
server/tests/runtimeNetcodeContractFixtureHash.test.tsasserts by value the ack thresholds, the slow-consumer bytes, the shard caps withprovisional, and the durability knee, and pins the resolved netcode-contract hash.
Where the model lives
This page states the values. For the model behind them, read the Overload and Degradation Model. For the room clock, authority, replication lanes, durability, and rejoin that this overload response sits inside, read the Netcode Contract reference and the Netcode Model. The player-safe copy for each overload code is in the generated runtime error registry.