Skip to content

When Provisioning Fails

The match is real but you cannot get a server for it. Those players have already left the pool, so doing nothing strands them. Put them back; do not dump them out to the menu.

sequenceDiagram
    autonumber
    participant IM as IVK Match
    participant GB as Your Backend
    participant OR as Orchestrator
    actor P as Players

    IM->>GB: outcome: match.created { match_id, tickets, host }
    GB->>OR: CreateGameServer(host = host.preferred)
    OR-->>GB: error, capacity exhausted

    loop for each host in host.acceptable
        GB->>OR: CreateGameServer(host)
        OR-->>GB: error
    end

    Note over GB: No server available on any acceptable host
    GB->>IM: ReactivateTickets(queue_id, ticket_ids)
    IM-->>GB: failed_ticket_ids: []
    Note over IM: Tickets are back in the pool,<br/>keeping their original created_at
    GB-->>P: still searching (no error shown)

Three things make this work.

host.acceptable is a fallback list you get for free

Section titled “host.acceptable is a fallback list you get for free”

Every host in engine_output.host.acceptable is tolerable to every ticket in the match, not just some of them. Walk it before giving up. A capacity failure in one region is common; a capacity failure in every region a match can tolerate is rare.

ReactivateTickets preserves queue position

Section titled “ReactivateTickets preserves queue position”

Reactivated tickets keep their original created_at, so a player who already waited 90 seconds does not go to the back of the line. As far as the player is concerned nothing happened. They were never told a match was found, and they are still searching.

That is why the player-facing message here is “still searching” rather than an error. The failure is yours to absorb.

terminal_retention_ttl_secs is your compensation window

Section titled “terminal_retention_ttl_secs is your compensation window”

A resolved ticket can only be reactivated while it is still retained. Any ticket returned in failed_ticket_ids was already evicted and needs a fresh CreateTicket instead.

Set pool.tickets.terminal_retention_ttl_secs above your orchestrator’s worst-case provisioning timeout plus the time it takes to walk the fallback list. The default of 300 s suits most setups; use 3600 s where provisioning can be slow.

If you get a non-empty failed_ticket_ids, treat it as a signal that the window is too short for your provisioning path, not just as a one-off.

Receiving match.created and neither honouring nor compensating for it is never acceptable. Every other ticket in that match is waiting on you. If your handler crashes mid-provisioning the outcome will be redelivered, since delivery is at-least-once, so an idempotent handler keyed on Match.id recovers on its own.

See Matchmaking API § Reactivate tickets.