package proxy import ( "errors" "sync/atomic" ) const counterMask = uint64(1<<32 - 1) var ( ErrReservationCommitted = errors.New("reservation is already committed") ErrReservationFinished = errors.New("reservation is already finished") ) // CapacityInvariantViolation is a fixed lifecycle violation category. It // deliberately excludes Proxy and request identifiers so observers can export // bounded metrics safely. type CapacityInvariantViolation string const ( CapacityInvariantCommitAlreadyCommitted CapacityInvariantViolation = "commit_already_committed" CapacityInvariantCommitFinished CapacityInvariantViolation = "commit_finished" CapacityInvariantCancelFinished CapacityInvariantViolation = "cancel_finished" CapacityInvariantReleaseBeforeCommit CapacityInvariantViolation = "release_before_commit" CapacityInvariantReleaseFinished CapacityInvariantViolation = "release_finished" ) type CapacityInvariant struct { Violation CapacityInvariantViolation } // CapacityInvariantObserver is a transport-free hook for lifecycle misuse. // Implementations must tolerate concurrent calls and must not block request // handling. type CapacityInvariantObserver interface { ObserveCapacityInvariant(CapacityInvariant) } type Capacity struct { max atomic.Uint32 counters atomic.Uint64 reservationEnabled atomic.Bool reserving atomic.Int64 configuredObserver *activityObserver observer atomic.Pointer[activityObserver] invariants CapacityInvariantObserver } type activityObserver struct{ notify func(bool) } func NewCapacity(max int64) *Capacity { return NewCapacityWithObservers(max, nil, nil) } // NewCapacityWithActivityObserver reports successful zero-to-nonzero and // nonzero-to-zero transitions. The observer must tolerate concurrent calls. func NewCapacityWithActivityObserver(max int64, observer func(nonzero bool)) *Capacity { return NewCapacityWithObservers(max, observer, nil) } // NewCapacityWithObservers creates an atomic capacity with optional local // activity and invariant observers. The invariant observer is only called on // invalid reservation lifecycle transitions. func NewCapacityWithObservers( max int64, activity func(nonzero bool), invariants CapacityInvariantObserver, ) *Capacity { capacity := &Capacity{} if max < 0 || max > int64(counterMask) { max = 0 } capacity.max.Store(uint32(max)) capacity.reservationEnabled.Store(true) capacity.invariants = invariants if activity != nil { capacity.configuredObserver = &activityObserver{notify: activity} capacity.observer.Store(capacity.configuredObserver) } return capacity } // SetActivityObservationEnabled lets snapshot ownership disable callbacks for // current Proxies and enable them only while a runtime is retired and draining. func (c *Capacity) SetActivityObservationEnabled(enabled bool) { if c == nil || c.configuredObserver == nil { return } if enabled { c.observer.Store(c.configuredObserver) return } c.observer.Store(nil) } // SetReservationEnabled gates new reservations without changing existing // Active or Reserved counters. It is used while a snapshot retires a Proxy. func (c *Capacity) SetReservationEnabled(enabled bool) { if c == nil { return } c.reservationEnabled.Store(enabled) } // Reclaimable reports whether a retired capacity can be forgotten without // losing an in-flight reservation or draining runtime counter. func (c *Capacity) Reclaimable() bool { if c == nil || c.reservationEnabled.Load() || c.reserving.Load() != 0 { return false } active, reserved, _ := c.Counters() return active == 0 && reserved == 0 } func (c *Capacity) SetMax(max int64) bool { if max < 0 || max > int64(counterMask) { return false } c.max.Store(uint32(max)) return true } func (c *Capacity) Max() int64 { return int64(c.max.Load()) } func (c *Capacity) Counters() (active, reserved, maximum int64) { if c == nil { return 0, 0, 0 } packed := c.counters.Load() activeCounter, reservedCounter := unpack(packed) return int64(activeCounter), int64(reservedCounter), int64(c.max.Load()) } func (c *Capacity) Reserve() (*Reservation, bool) { if c == nil || !c.beginReservation() { return nil, false } defer c.reserving.Add(-1) for { if !c.reservationEnabled.Load() { return nil, false } current := c.counters.Load() active, reserved := unpack(current) if active+reserved >= c.max.Load() { return nil, false } next := pack(active, reserved+1) if c.counters.CompareAndSwap(current, next) { if !c.reservationEnabled.Load() { c.cancel() return nil, false } if active+reserved == 0 { if observer := c.observer.Load(); observer != nil { observer.notify(true) } } return &Reservation{capacity: c}, true } } } func (c *Capacity) beginReservation() bool { if !c.reservationEnabled.Load() { return false } c.reserving.Add(1) if c.reservationEnabled.Load() { return true } c.reserving.Add(-1) return false } func (c *Capacity) Active() int64 { active, _ := unpack(c.counters.Load()) return int64(active) } func (c *Capacity) Reserved() int64 { _, reserved := unpack(c.counters.Load()) return int64(reserved) } func (c *Capacity) commit() { for { current := c.counters.Load() active, reserved := unpack(current) if reserved == 0 { return } if c.counters.CompareAndSwap(current, pack(active+1, reserved-1)) { return } } } func (c *Capacity) cancel() { for { current := c.counters.Load() active, reserved := unpack(current) if reserved == 0 { return } next := pack(active, reserved-1) if c.counters.CompareAndSwap(current, next) { if active+reserved == 1 { if observer := c.observer.Load(); observer != nil { observer.notify(false) } } return } } } func (c *Capacity) release() { for { current := c.counters.Load() active, reserved := unpack(current) if active == 0 { return } next := pack(active-1, reserved) if c.counters.CompareAndSwap(current, next) { if active+reserved == 1 { if observer := c.observer.Load(); observer != nil { observer.notify(false) } } return } } } func (c *Capacity) observeInvariant(violation CapacityInvariantViolation) { if c == nil || c.invariants == nil { return } c.invariants.ObserveCapacityInvariant(CapacityInvariant{Violation: violation}) } func pack(active, reserved uint32) uint64 { return uint64(reserved)<<32 | uint64(active) } func unpack(value uint64) (active, reserved uint32) { return uint32(value & counterMask), uint32(value >> 32) } type Reservation struct { capacity *Capacity state atomic.Uint32 } const ( reservationOpen uint32 = iota reservationCommitted reservationCanceled reservationReleased ) func (r *Reservation) Commit() error { if !r.state.CompareAndSwap(reservationOpen, reservationCommitted) { if r.state.Load() == reservationCommitted { r.capacity.observeInvariant(CapacityInvariantCommitAlreadyCommitted) return ErrReservationCommitted } r.capacity.observeInvariant(CapacityInvariantCommitFinished) return ErrReservationFinished } r.capacity.commit() return nil } func (r *Reservation) Cancel() error { if !r.state.CompareAndSwap(reservationOpen, reservationCanceled) { r.capacity.observeInvariant(CapacityInvariantCancelFinished) return ErrReservationFinished } r.capacity.cancel() return nil } func (r *Reservation) Release() error { if !r.state.CompareAndSwap(reservationCommitted, reservationReleased) { if r.state.Load() == reservationOpen { r.capacity.observeInvariant(CapacityInvariantReleaseBeforeCommit) } else { r.capacity.observeInvariant(CapacityInvariantReleaseFinished) } return ErrReservationFinished } r.capacity.release() return nil }