fix(multiplayer): publish stalled allocation recovery

This commit is contained in:
Josh Creek
2026-09-01 18:19:28 +01:00
parent 7c044b7094
commit 76c1c3d600
4 changed files with 47 additions and 4 deletions
+25 -4
View File
@@ -27,7 +27,7 @@ const ExpireStalledAllocationsSQL = `WITH stalled AS (
), failed AS (
UPDATE matches SET state = 'FAILED', revision = revision + 1
WHERE match_id IN (SELECT match_id FROM stalled)
RETURNING match_id
RETURNING match_id, revision
), released AS (
UPDATE match_participants SET participation_active = FALSE
WHERE match_id IN (SELECT match_id FROM failed) AND participation_active
@@ -36,8 +36,26 @@ const ExpireStalledAllocationsSQL = `WITH stalled AS (
UPDATE queue_tickets SET state = 'QUEUED', expires_at = $3, revision = revision + 1
WHERE ticket_id IN (SELECT ticket_id FROM released)
RETURNING ticket_id
), events AS (
INSERT INTO outbox (event_id, aggregate_type, aggregate_id, revision, event_type, payload)
SELECT 'stalled-allocation:' || failed.match_id || ':' || failed.revision,
'match', failed.match_id, failed.revision, 'state_changed',
jsonb_build_object(
'event', 'state_changed',
'revision', failed.revision,
'resource_id', failed.match_id,
'occurred_at', $4,
'state', 'FAILED',
'match_id', failed.match_id,
'player_ids', COALESCE((
SELECT jsonb_agg(mp.player_id ORDER BY mp.player_id)
FROM match_participants mp WHERE mp.match_id = failed.match_id
), '[]'::jsonb)
)
FROM failed
ON CONFLICT DO NOTHING
)
SELECT (SELECT count(*) FROM failed), (SELECT count(*) FROM requeued)`
SELECT (SELECT count(*) FROM failed), (SELECT count(*) FROM requeued), (SELECT count(*) FROM events)`
// ExpireStalledAllocations reclaims up to `limit` matches whose
// created_at is at or before `now - deadline` and are still stuck in one of
@@ -48,12 +66,15 @@ func ExpireStalledAllocations(ctx context.Context, db *sql.DB, now time.Time, de
if db == nil || now.IsZero() || deadline <= 0 || limit < 1 || limit > 1000 {
return 0, fmt.Errorf("invalid stalled-allocation maintenance arguments")
}
var matches, requeued int
var matches, requeued, events int
err := RunSerializable(ctx, db, DefaultSerializableAttempts, func(ctx context.Context, tx *sql.Tx) error {
return tx.QueryRowContext(ctx, ExpireStalledAllocationsSQL, now.Add(-deadline), limit, now.Add(domain.QueueExpiryWindow)).Scan(&matches, &requeued)
return tx.QueryRowContext(ctx, ExpireStalledAllocationsSQL, now.Add(-deadline), limit, now.Add(domain.QueueExpiryWindow), now).Scan(&matches, &requeued, &events)
})
if err != nil {
return 0, err
}
if events != matches {
return 0, fmt.Errorf("stalled-allocation outbox count %d does not match reclaimed matches %d", events, matches)
}
return matches, nil
}