Backpressure & Budgets
A slow or hostile client costs itself, never the tick or the other sessions.
Status: โ Implemented ยท Visibility: Public ยท Level: ๐ฃ Advanced ยท Category: Subscriptions
๐ฏ What it solves
Clients are slow, lossy, far away or malicious. A server that queues frames for a slow client grows without bound; one that waits for it stalls everybody; one that accepts whatever a client sends can be driven off its tick.
โ๏ธ How it works (in brief)
- Skip, never queue. A session has two frame slots (one in flight, one ready). With both busy it is skipped: nothing is gathered
for it and nothing about it moves. Its next frame carries the union of everything it missed, replayed from an 8-tick push log; past
it, a
RESETre-delivers its view. - Rate classes. A session that keeps being skipped is served every 2nd, then every 4th tick, then closed with 1013 at
CloseStalledAfter. A profile can also be served every 2 or 4 ticks by declaration (Every). - Acknowledgement-based lag. A send completing is not proof of delivery; clients report their last applied tick in
PING(4 Hz), and a session too far behind is skipped. A client that stops pinging is closed with 4001. - Per-session byte budget (
Session(id).SetBudget(bytesPerSecond)orSessionLimits.BytesPerSecond): the engine raises the session's detail level โ longer periods for far bands, a smaller enter budget โ and, at the last level, shrinks its radius; it never drops state. - Overload. When the runtime is overloaded, every Sphere session is served a level lower and every profile half as often.
- Inbound. Each session's inbound byte budget (
IngressBytesPerSecond, required with commands) refuses over-budgetCOMMANDSwhole; refused commands pastAbuseRefusalsPerWindowforAbuseWindowsadjacent windows close the session with 1008. - Pools. Frames and per-entity state live in native pools under
FramePoolBudgetBytesandStatePoolBudgetBytes; an exhausted frame pool skips sessions (and they catch up), never allocates.
| Option | Default | Effect |
|---|---|---|
CloseStalledAfter |
3 s | Skip run after which a session is closed with 1013 (degraded first) |
LagSkipRttAllowanceMs |
250 | RTT allowance of the acknowledgement-based lag skip |
FrameBytes |
256 KiB | Largest frame a session may be sent |
FramePoolBudgetBytes / StatePoolBudgetBytes |
256 MiB / 256 MiB | Native pool ceilings |
IngressPoolBudgetBytes |
64 MiB | Inbound rings, all sessions |
โ ๏ธ Guarantees & limits
- A slower session loses rate, never state; records are absolute and skips are unions (SUB-03).
- An idle tick is not a skip (SUB-15).
- An exhausted state pool leaves a pushed cluster unprojected for that tick โ a policy for it is not built yet.
- Connection churn is not rate-limited by the engine. Measured on the SWG demo, ~1 700 connect/close cycles a second from one host raise the tick's p99 severalfold; put connection-rate limiting in front of a public endpoint.
๐งช Tests
- SendPumpTests โ skip, degrade, close
- PushLodLevelTests โ detail levels under a budget
- IngressHardeningTests โ inbound budget and abuse
๐ Related
- Related feature: Overload Management
- Commands & acknowledgements