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.
sweepSeconds | Wakes per waiting minute, per game | What it buys |
|---|---|---|
| 1 | 60 | Bands widen almost continuously. Pairing latency is dominated by who arrives, not by the sweep |
| 5 (default) | 12 | A player waits at most 5 seconds past the moment their band grew wide enough |
| 15 | 4 | A 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/queue | Enter |
GET /matchmaking/games/:game/queue | Where you stand |
DELETE /matchmaking/games/:game/queue | Leave |
When a match forms, the player learns through presence — a match_found event carrying the session id — rather than by polling.