---
title: "Reference: Backpressure and Degradation"
description: "The runtime overload surfaces, invariants, and pinned numbers - the backpressure summary, the per-room and per-shard degradation ladders, the state-stream governor, the slow-consumer machine, and the capacity floor - each bound to a symbol and a proving test."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/reference/backpressure-degradation/
---

# Reference: Backpressure and Degradation

{% version engineVersion="v1.0.232" coordinate="docs-public.v0" /%}

_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](../explanation/backpressure-degradation.md); this page states the values, that page states the why. For the wider runtime this sits inside, read the [Netcode Contract reference](netcode-contract.md).

## 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.

```math
ackLag(c) = \max(0,\ latestServerSeq - c.lastAckedServerSeq)
```

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:

```math
state = \max\nolimits_{rank}\big(\ ackLagState,\ degradedCountState,\ gatewayPressureState\ \big),\quad none < watch < throttling < shedding
```

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:

```math
tickRatio = \frac{tickP99Ms}{\max(1,\ frameBudgetMs)}
```

```math
rung = \begin{cases}
5 & tickRatio \ge 2 \ \lor\ ring \ge 1\\
4 & tickRatio \ge 1.5 \ \lor\ ring \ge 0.95 \ \lor\ roomCount \ge roomCapacity\\
3 & tickRatio \ge 1.25 \ \lor\ ring \ge 0.85\\
2 & tickRatio \ge 1 \ \lor\ ring \ge 0.7\\
1 & tickRatio \ge 0.8 \ \lor\ ring \ge 0.5\\
0 & \text{otherwise}
\end{cases}
```

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).

{% warning severity="caution" title="The per-shard ladder is not wired into a live actuator" %}
`evaluateShardDegradation` has no caller anywhere in `server/src`; only `shardPlacementCapacityLadder.test.ts` invokes it. The shard host gathers a shard's load signals through `requestGovernor` in `shard/shardHost.ts`, but nothing evaluates them into a live shed action, and the production tick-authority path is unsharded (see the capacity floor). Treat the rungs `thin_snapshots`, `reduce_snapshot_rate`, and `pause_non_essential_lanes` as declared-but-inert today: the typed codes exist and the logic is unit-tested, but the wiring that would make a running shard shed on its own signals was not found.
{% /warning %}

## 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](netcode-contract.md); 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 |

{% generated-reference file="docs/spec/generated/error-catalog.md" label="Runtime error registry" /%}

## 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.** `evaluateShardDegradation` has no `server/src` caller, and the shard host collects load signals through `requestGovernor` in `shard/shardHost.ts` but nothing evaluates them into a live shed action.
- **The live tick-authority path is unsharded.** `assertRoomPlacementWithinInstanceBudget` in `service.ts` places with the unsharded flag, so the per-shard scopes are never consulted in production, and `ShardHost` (the only sharded `placeRoom` caller) is instantiated only in tests.
- **The capacity caps are provisional placeholders, not measured service levels.** `provisional: true` on `RUNTIME_SHARD_CAPACITY_POLICY` is 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_ORDER` performs no action; its rungs are realized by separate modules.
- **The backpressure summary is observational.** `summarizeRuntimeBackpressure` classifies a room and drives no shed on its own path.
- **The durability knee gates only load-test plans.** `RUNTIME_LOAD_TEST_POLICY` is consumed by `loadTestRunner.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.md` remain 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.ts` pins the `RUNTIME_LOAD_DEGRADATION_ORDER` action sequence.
- **Per-shard ladder**: `server/tests/shard/shardPlacementCapacityLadder.test.ts` drives `evaluateShardDegradation` up 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 at `roomCount` at or above `roomCapacity`), 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.ts` proves each scope's isolated reason and hint, the accept-with-`downsample` behavior at the degrade ratio, and command-versus-stream budget isolation under floods.
- **Placement refusal**: `server/tests/runtimeLocalTickAuthorityCapacity.test.ts` proves an over-budget room never joins the tick loop and the refusal carries category capacity.
- **Constant drift gate**: `server/tests/runtimeNetcodeContractFixtureHash.test.ts` asserts by value the ack thresholds, the slow-consumer bytes, the shard caps with `provisional`, 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](../explanation/backpressure-degradation.md). For the room clock, authority, replication lanes, durability, and rejoin that this overload response sits inside, read the [Netcode Contract reference](netcode-contract.md) and [the Netcode Model](../explanation/netcode-model.md). The player-safe copy for each overload code is in the generated runtime error registry.
