The waiting queue

How a pairing happens

A match forms on enqueue when an opponent is already waiting. It does not wait for a sweep. The queue is one of four ways into a session, and the only one that reads skill.

The sweep exists to widen bands, not to pair.

The bands widen

queue: { initialBand: 100, widenPerSecond: 50, maxWaitSeconds: 120, sweepSeconds: 5 }

A player first matches only opponents within initialBand of their skill.

The band grows by widenPerSecond for every second they wait.

After maxWaitSeconds it is unbounded — any opponent in their region qualifies.

Skill comes from the rating capability, read through a seam. Omit skillPool and the queue buckets on region alone.

Region comes from Cloudflare’s own edge geolocation, not from anything the client says.

One alarm, and the cost is what waiting costs

One Durable Object per game and region holds the queue; what that costs is a function of how long people wait rather than how many play. Every field above is in the reference.

While anyone is waiting, a single alarm is armed at now + sweepSeconds. It wakes the object, re-attempts pairing, widens every waiting player’s band, and re-arms.

Each wake is a request plus the duration of a short handler.

sweepSecondsWakes per waiting minute, per gameWhat it buys
160Bands widen almost continuously. Pairing latency is dominated by who arrives, not by the sweep
5 (default)12A player waits at most 5 seconds past the moment their band grew wide enough
154A fifth of the default’s wakes. Noticeable on a thin queue, where the band is what unblocks the match

Nothing is held in memory

The waiting list is read fresh and Zod-parsed on every handler, so an eviction costs a read and nothing else.

Holding state in memory to avoid that read would cost the hibernation that makes the whole object cheap — a Durable Object pinned in memory bills duration continuously.

That is the same discipline the presence object keeps, and for the same reason.

Two platform facts this inherits

WebSocket messages bill at a 20:1 ratio.

Duration bills the full 128 MB, and only while an object is awake.

Both are documented on the multiplayer side, and both are why the design refuses timers and in-memory registries.

What the queue is not

It is not a lobby service. There is no player list leaving your infrastructure, and nobody operates it but you.

It is not a ranking system. It reads skill; it does not compute or update it. That is the rating capability’s job, and its absence degrades the queue to region-only rather than breaking it.

The routes

Route
POST /matchmaking/games/:game/queueEnter
GET /matchmaking/games/:game/queueWhere you stand
DELETE /matchmaking/games/:game/queueLeave

When a match forms, the player learns through presence — a match_found event carrying the session id — rather than by polling.

ESC