feat: gate assignment publication on allocation

This commit is contained in:
Josh Creek
2026-08-31 21:24:19 +01:00
parent 88b5ffedb2
commit 726fe1ce2e
4 changed files with 71 additions and 7 deletions
+30 -3
View File
@@ -49,16 +49,18 @@ type Allocator struct {
mu sync.Mutex
servers map[string]ReadyServer
allocations map[string]Allocation
assignments map[string]Assignment
requestHashes map[string][32]byte
}
var (
ErrNoCapacity = fmt.Errorf("no compatible ready server")
ErrAllocationInput = fmt.Errorf("invalid allocation request")
ErrNoCapacity = fmt.Errorf("no compatible ready server")
ErrAllocationInput = fmt.Errorf("invalid allocation request")
ErrAllocationNotFound = fmt.Errorf("allocation not found")
)
func NewAllocator(servers []ReadyServer) (*Allocator, error) {
a := &Allocator{servers: make(map[string]ReadyServer, len(servers)), allocations: make(map[string]Allocation), requestHashes: make(map[string][32]byte)}
a := &Allocator{servers: make(map[string]ReadyServer, len(servers)), allocations: make(map[string]Allocation), assignments: make(map[string]Assignment), requestHashes: make(map[string][32]byte)}
for _, server := range servers {
if server.ServerID == "" || server.Region == "" || server.Build == "" || server.Protocol <= 0 || (server.Transport != "enet" && server.Transport != "steam_sdr") || server.State != ServerReady {
return nil, fmt.Errorf("%w: invalid ready server", ErrAllocationInput)
@@ -106,6 +108,31 @@ func (a *Allocator) Allocate(request AllocationRequest, now time.Time) (Allocati
return allocation, nil
}
// PublishAssignment is the allocation-to-client boundary. It holds the same
// allocator lock as the claim and exposes no assignment until the allocated
// server, complete compatibility tuple, endpoint, and manifest signature all
// verify. The returned assignment is stable across an identical retry.
func (a *Allocator) PublishAssignment(allocationID string, manifest AllocationManifest, endpoint string, signature []byte, verify func([]byte, []byte) bool) (Assignment, error) {
a.mu.Lock()
defer a.mu.Unlock()
allocation, ok := a.allocations[allocationID]
if !ok {
return Assignment{}, ErrAllocationNotFound
}
assignment, err := VerifyAssignment(allocation, manifest, endpoint, signature, verify)
if err != nil {
return Assignment{}, err
}
if prior, exists := a.assignments[allocationID]; exists {
if prior != assignment {
return Assignment{}, ErrConflict
}
return prior, nil
}
a.assignments[allocationID] = assignment
return assignment, nil
}
func validateAllocationRequest(request AllocationRequest) error {
if request.AllocationID == "" || request.MatchID == "" || request.Region == "" || request.Build == "" || request.Protocol <= 0 || (request.Transport != "enet" && request.Transport != "steam_sdr") {
return ErrAllocationInput