Docs/Hosted Matchmaking/Tenant Server Pattern
Hosted Matchmaking

Tenant server pattern

A reference sketch, not the only valid shape — the pattern every real tenant tracker built and tested this way converged on: one persistent process, ticking on a fixed interval, assembling and starting matches server-side.

js
setInterval(async () => {
  if (tickInFlight) return;      // guard against overlapping ticks
  tickInFlight = true;
  try {
    const waiting = getWaitingPlayers();       // your own queue/lobby
    if (waiting.length < MIN_PLAYERS) return;

    const group = waiting.splice(0, GROUP_SIZE);
    const { ticketId } = await post('/matchmaking/hosted/ticket?game=checkers');
    for (const player of group) {
      await post(`/matchmaking/hosted/ticket/${ticketId}/players?game=checkers`,
        { playerId: player.id });
    }
    await post(`/matchmaking/hosted/ticket/${ticketId}/promote?game=checkers`);
    const { group: promoted } = await getLastPromotedGroup();
    await post('/matchmaking/hosted/start-match?game=checkers',
      { groupIds: [promoted.id] });
  } finally {
    tickInFlight = false;
  }
}, 3000);

Partition by whatever varies

A team-size game (2v2 vs 3v3) or a variable-player-count game (2, 3, or 4-player) needs one tracker partitioned by that dimension — a 2v2 lobby and a 3v3 lobby should never merge into the same ticket. Keep a separate waiting list per size, not one list with a size filter applied late.

Reentrancy is the real bug class here, confirmed directly by testing tenant trackers this session — a slow request from one interval tick overlapping the next produces duplicate ticket submissions. ThetickInFlight guard above isn't decorative.