Skip to content

When Provisioning Fails

The match is real but no server can be obtained for it. Those players have already left the pool, so doing nothing strands them. The integration returns them to the pool rather than sending them back to the menu.

sequenceDiagram
    autonumber
    participant IM as IVK Match
    participant GB as Game 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)

This relies on three things.

host.acceptable is a ready-made fallback list

Section titled “host.acceptable is a ready-made fallback list”

Every host in engine_output.host.acceptable is tolerable to every ticket in the match, not just some of them. Walk it before declaring provisioning failed. 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 absorbed by the backend.

terminal_retention_ttl_secs is the compensation window

Section titled “terminal_retention_ttl_secs is the 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 the 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.

A non-empty failed_ticket_ids indicates that the window is too short for the provisioning path, rather than a one-off.

A match.created that is neither honoured nor compensated for leaves every other ticket in that match waiting. If the outcome 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.