mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
fix(multiplayer): persist arena identity on allocations
This commit is contained in:
@@ -156,7 +156,7 @@ func (c Client) RecoverAllocation(ctx context.Context, request domain.Allocation
|
||||
if err != nil {
|
||||
return AllocatedServer{}, false, err
|
||||
}
|
||||
recovered = AllocatedServer{Allocation: domain.Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: item.Metadata.Name, Region: request.Region, Build: request.Build, Protocol: request.Protocol, Transport: request.Transport, State: domain.ServerAllocated, AllocatedAt: now}, Endpoint: net.JoinHostPort(item.Status.Address, strconv.Itoa(port)), GameServer: item.Metadata.Name}
|
||||
recovered = AllocatedServer{Allocation: domain.Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: item.Metadata.Name, Region: request.Region, Build: request.Build, Protocol: request.Protocol, ArenaPath: request.ArenaPath, Transport: request.Transport, State: domain.ServerAllocated, AllocatedAt: now}, Endpoint: net.JoinHostPort(item.Status.Address, strconv.Itoa(port)), GameServer: item.Metadata.Name}
|
||||
found = true
|
||||
}
|
||||
return recovered, found, nil
|
||||
@@ -291,7 +291,7 @@ func (c Client) Allocate(ctx context.Context, request domain.AllocationRequest,
|
||||
if err != nil {
|
||||
return AllocatedServer{}, err
|
||||
}
|
||||
return AllocatedServer{Allocation: domain.Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: decoded.Status.GameServerName, Region: request.Region, Build: request.Build, Protocol: request.Protocol, Transport: request.Transport, State: domain.ServerAllocated, AllocatedAt: now}, Endpoint: net.JoinHostPort(decoded.Status.Address, strconv.Itoa(port)), GameServer: decoded.Status.GameServerName}, nil
|
||||
return AllocatedServer{Allocation: domain.Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: decoded.Status.GameServerName, Region: request.Region, Build: request.Build, Protocol: request.Protocol, ArenaPath: request.ArenaPath, Transport: request.Transport, State: domain.ServerAllocated, AllocatedAt: now}, Endpoint: net.JoinHostPort(decoded.Status.Address, strconv.Itoa(port)), GameServer: decoded.Status.GameServerName}, nil
|
||||
}
|
||||
|
||||
func (c Client) endpoint() (string, error) {
|
||||
|
||||
@@ -42,6 +42,7 @@ type Allocation struct {
|
||||
Region string
|
||||
Build string
|
||||
Protocol int
|
||||
ArenaPath string
|
||||
Transport string
|
||||
State ServerLifecycle
|
||||
AllocatedAt time.Time
|
||||
@@ -104,7 +105,7 @@ func (a *Allocator) Allocate(request AllocationRequest, now time.Time) (Allocati
|
||||
server := a.servers[ids[0]]
|
||||
server.State = ServerAllocated
|
||||
a.servers[server.ServerID] = server
|
||||
allocation := Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: server.ServerID, Region: server.Region, Build: server.Build, Protocol: server.Protocol, Transport: server.Transport, State: ServerAllocated, AllocatedAt: now}
|
||||
allocation := Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: server.ServerID, Region: server.Region, Build: server.Build, Protocol: server.Protocol, ArenaPath: request.ArenaPath, Transport: server.Transport, State: ServerAllocated, AllocatedAt: now}
|
||||
a.allocations[request.AllocationID] = allocation
|
||||
a.requestHashes[request.AllocationID] = digest
|
||||
return allocation, nil
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Keep arena identity in the durable provider allocation record so retries
|
||||
-- and provider recovery compare the complete match compatibility tuple.
|
||||
ALTER TABLE allocations
|
||||
ADD COLUMN arena_path TEXT;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE allocations
|
||||
DROP COLUMN IF EXISTS arena_path;
|
||||
@@ -8,6 +8,7 @@ SQL = (Path(__file__).parent / "0001_initial.sql").read_text()
|
||||
ASSIGNMENTS_SQL = (Path(__file__).parent / "0002_assignments.sql").read_text()
|
||||
QUOTAS_SQL = (Path(__file__).parent / "0007_allocation_quotas.sql").read_text()
|
||||
ARENAS_SQL = (Path(__file__).parent / "0008_match_arena_paths.sql").read_text()
|
||||
ALLOCATION_ARENAS_SQL = (Path(__file__).parent / "0009_allocation_arena_paths.sql").read_text()
|
||||
|
||||
|
||||
class MigrationTest(unittest.TestCase):
|
||||
@@ -68,6 +69,10 @@ class MigrationTest(unittest.TestCase):
|
||||
):
|
||||
self.assertIn(fragment, ARENAS_SQL)
|
||||
|
||||
def test_provider_allocation_retains_arena_identity(self):
|
||||
self.assertIn("ALTER TABLE allocations", ALLOCATION_ARENAS_SQL)
|
||||
self.assertIn("ADD COLUMN arena_path TEXT", ALLOCATION_ARENAS_SQL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -175,7 +175,7 @@ func FindProviderAllocation(ctx context.Context, db *sql.DB, request domain.Allo
|
||||
}
|
||||
var allocation domain.Allocation
|
||||
var digest []byte
|
||||
err := db.QueryRowContext(ctx, SelectAllocationSQL, request.AllocationID).Scan(&allocation.AllocationID, &allocation.MatchID, &allocation.ServerID, &allocation.Region, &allocation.Build, &allocation.Protocol, &allocation.Transport, &allocation.AllocatedAt, &digest)
|
||||
err := db.QueryRowContext(ctx, SelectAllocationSQL, request.AllocationID).Scan(&allocation.AllocationID, &allocation.MatchID, &allocation.ServerID, &allocation.Region, &allocation.Build, &allocation.Protocol, &allocation.ArenaPath, &allocation.Transport, &allocation.AllocatedAt, &digest)
|
||||
if err == sql.ErrNoRows {
|
||||
return domain.Allocation{}, false, nil
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func FindProviderAllocation(ctx context.Context, db *sql.DB, request domain.Allo
|
||||
return domain.Allocation{}, false, err
|
||||
}
|
||||
want := allocationRequestDigest(request)
|
||||
if !bytes.Equal(digest, want[:]) || allocation.MatchID != request.MatchID || allocation.Region != request.Region || allocation.Build != request.Build || allocation.Protocol != request.Protocol || allocation.Transport != request.Transport {
|
||||
if !bytes.Equal(digest, want[:]) || allocation.MatchID != request.MatchID || allocation.Region != request.Region || allocation.Build != request.Build || allocation.Protocol != request.Protocol || allocation.ArenaPath != request.ArenaPath || allocation.Transport != request.Transport {
|
||||
return domain.Allocation{}, false, domain.ErrConflict
|
||||
}
|
||||
allocation.State = domain.ServerAllocated
|
||||
|
||||
@@ -31,11 +31,11 @@ WHERE server_id = (
|
||||
RETURNING server_id`
|
||||
|
||||
const InsertAllocationSQL = `INSERT INTO allocations
|
||||
(allocation_id, match_id, server_id, region, build, protocol_version, transport, request_digest, state, allocated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'ALLOCATED', $9)`
|
||||
(allocation_id, match_id, server_id, region, build, protocol_version, arena_path, transport, request_digest, state, allocated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 'ALLOCATED', $10)`
|
||||
|
||||
const SelectAllocationSQL = `SELECT allocation_id, match_id, server_id, region, build,
|
||||
protocol_version, transport, allocated_at, request_digest
|
||||
protocol_version, arena_path, transport, allocated_at, request_digest
|
||||
FROM allocations WHERE allocation_id = $1`
|
||||
|
||||
const ProviderServerClaimSQL = `UPDATE game_servers SET state = 'ALLOCATED', updated_at = $6
|
||||
@@ -63,7 +63,7 @@ func ClaimAllocation(ctx context.Context, db *sql.DB, request domain.AllocationR
|
||||
err := RunSerializable(ctx, db, DefaultSerializableAttempts, func(ctx context.Context, tx *sql.Tx) error {
|
||||
var prior domain.Allocation
|
||||
var priorDigest []byte
|
||||
err := tx.QueryRowContext(ctx, SelectAllocationSQL, request.AllocationID).Scan(&prior.AllocationID, &prior.MatchID, &prior.ServerID, &prior.Region, &prior.Build, &prior.Protocol, &prior.Transport, &prior.AllocatedAt, &priorDigest)
|
||||
err := tx.QueryRowContext(ctx, SelectAllocationSQL, request.AllocationID).Scan(&prior.AllocationID, &prior.MatchID, &prior.ServerID, &prior.Region, &prior.Build, &prior.Protocol, &prior.ArenaPath, &prior.Transport, &prior.AllocatedAt, &priorDigest)
|
||||
if err == nil {
|
||||
if !bytes.Equal(priorDigest, digest[:]) {
|
||||
return domain.ErrConflict
|
||||
@@ -85,8 +85,8 @@ func ClaimAllocation(ctx context.Context, db *sql.DB, request domain.AllocationR
|
||||
}
|
||||
return err
|
||||
}
|
||||
allocation = domain.Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: serverID, Region: request.Region, Build: request.Build, Protocol: request.Protocol, Transport: request.Transport, State: domain.ServerAllocated, AllocatedAt: now}
|
||||
_, err = tx.ExecContext(ctx, InsertAllocationSQL, request.AllocationID, request.MatchID, serverID, request.Region, request.Build, request.Protocol, request.Transport, digest[:], now)
|
||||
allocation = domain.Allocation{AllocationID: request.AllocationID, MatchID: request.MatchID, ServerID: serverID, Region: request.Region, Build: request.Build, Protocol: request.Protocol, ArenaPath: request.ArenaPath, Transport: request.Transport, State: domain.ServerAllocated, AllocatedAt: now}
|
||||
_, err = tx.ExecContext(ctx, InsertAllocationSQL, request.AllocationID, request.MatchID, serverID, request.Region, request.Build, request.Protocol, request.ArenaPath, request.Transport, digest[:], now)
|
||||
return err
|
||||
})
|
||||
return allocation, err
|
||||
@@ -97,7 +97,7 @@ func ClaimAllocation(ctx context.Context, db *sql.DB, request domain.AllocationR
|
||||
// Agones has already selected the server; no client-facing assignment may use
|
||||
// the result until this exact tuple is durably recorded.
|
||||
func RecordProviderAllocation(ctx context.Context, db *sql.DB, allocation domain.Allocation, now time.Time) (domain.Allocation, error) {
|
||||
request := domain.AllocationRequest{AllocationID: allocation.AllocationID, MatchID: allocation.MatchID, Region: allocation.Region, Build: allocation.Build, Protocol: allocation.Protocol, Transport: allocation.Transport}
|
||||
request := domain.AllocationRequest{AllocationID: allocation.AllocationID, MatchID: allocation.MatchID, Region: allocation.Region, Build: allocation.Build, Protocol: allocation.Protocol, ArenaPath: allocation.ArenaPath, Transport: allocation.Transport}
|
||||
if !validAllocationInput(db, request, now) || allocation.State != domain.ServerAllocated || allocation.ServerID == "" {
|
||||
return domain.Allocation{}, domain.ErrAllocationInput
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func RecordProviderAllocation(ctx context.Context, db *sql.DB, allocation domain
|
||||
err := RunSerializable(ctx, db, DefaultSerializableAttempts, func(ctx context.Context, tx *sql.Tx) error {
|
||||
var prior domain.Allocation
|
||||
var priorDigest []byte
|
||||
err := tx.QueryRowContext(ctx, SelectAllocationSQL, allocation.AllocationID).Scan(&prior.AllocationID, &prior.MatchID, &prior.ServerID, &prior.Region, &prior.Build, &prior.Protocol, &prior.Transport, &prior.AllocatedAt, &priorDigest)
|
||||
err := tx.QueryRowContext(ctx, SelectAllocationSQL, allocation.AllocationID).Scan(&prior.AllocationID, &prior.MatchID, &prior.ServerID, &prior.Region, &prior.Build, &prior.Protocol, &prior.ArenaPath, &prior.Transport, &prior.AllocatedAt, &priorDigest)
|
||||
if err == nil {
|
||||
if !bytes.Equal(priorDigest, digest[:]) || prior.ServerID != allocation.ServerID {
|
||||
return domain.ErrConflict
|
||||
@@ -133,7 +133,7 @@ func RecordProviderAllocation(ctx context.Context, db *sql.DB, allocation domain
|
||||
}
|
||||
recorded = allocation
|
||||
recorded.AllocatedAt = now
|
||||
_, err = tx.ExecContext(ctx, InsertAllocationSQL, allocation.AllocationID, allocation.MatchID, serverID, allocation.Region, allocation.Build, allocation.Protocol, allocation.Transport, digest[:], now)
|
||||
_, err = tx.ExecContext(ctx, InsertAllocationSQL, allocation.AllocationID, allocation.MatchID, serverID, allocation.Region, allocation.Build, allocation.Protocol, allocation.ArenaPath, allocation.Transport, digest[:], now)
|
||||
return err
|
||||
})
|
||||
return recorded, err
|
||||
|
||||
Reference in New Issue
Block a user