@@ -21,104 +21,84 @@ import (
2121 "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/types"
2222)
2323
24- // FlowRegistry is the complete interface for the global control plane. An implementation of this interface is the single
25- // source of truth for all flow control state and configuration.
24+ // FlowRegistry is the complete interface for the global flow control plane.
25+ // It composes the client-facing data path interface and the administrative interface. A concrete implementation of this
26+ // interface is the single source of truth for all flow control state.
2627//
27- // # Conformance
28+ // # Conformance: Implementations MUST be goroutine-safe.
29+ //
30+ // # Flow Lifecycle
31+ //
32+ // A flow instance, identified by its immutable `types.FlowKey`, has a lease-based lifecycle managed by this interface.
33+ // Any implementation MUST adhere to this lifecycle:
2834//
29- // All methods MUST be goroutine-safe. Implementations are expected to perform complex updates (e.g.,
30- // `RegisterOrUpdateFlow`) atomically.
35+ // 1. Lease Acquisition: A client calls Connect to acquire a lease. This signals that the flow is in use and protects
36+ // it from garbage collection. If the flow does not exist, it is created Just-In-Time (JIT).
37+ // 2. Active State: A flow is "Active" as long as its lease count is greater than zero.
38+ // 3. Lease Release: The client MUST call `Close()` on the returned `FlowConnection` to release the lease.
39+ // When the lease count drops to zero, the flow becomes "Idle".
40+ // 4. Garbage Collection: The implementation MUST automatically garbage collect a flow after it has remained
41+ // continuously Idle for a configurable duration.
3142//
3243// # System Invariants
3344//
3445// Concrete implementations MUST uphold the following invariants:
46+ //
3547// 1. Shard Consistency: All configured priority bands and registered flow instances must exist on every Active shard.
36- // Plugin instance types must be consistent for a given flow across all shards.
37- // 2. Flow Instance Uniqueness: Each unique `types.FlowKey` (`ID` + `Priority`) corresponds to exactly one managed flow
38- // instance.
39- // 3. Capacity Partitioning: Global and per-band capacity limits must be uniformly partitioned across all Active
48+ // 2. Capacity Partitioning: Global and per-band capacity limits must be uniformly partitioned across all Active
4049// shards.
41- //
42- // # Flow Lifecycle
43- //
44- // A flow instance (identified by its immutable `FlowKey`) has a simple lifecycle:
45- //
46- // - Registered: Known to the `FlowRegistry` via `RegisterOrUpdateFlow`.
47- // - Idle: Queues are empty across all Active and Draining shards.
48- // - Garbage Collected (Unregistered): The registry automatically garbage collects flows after they have remained Idle
49- // for a configurable duration.
50- //
51- // # Shard Lifecycle
52- //
53- // When a shard is decommissioned, it is marked inactive (`IsActive() == false`) to prevent new enqueues. The shard
54- // continues to drain and is deleted only after it is empty.
5550type FlowRegistry interface {
51+ FlowRegistryClient
5652 FlowRegistryAdmin
57- ShardProvider
5853}
5954
6055// FlowRegistryAdmin defines the administrative interface for the global control plane.
61- //
62- // # Dynamic Update Strategies
63- //
64- // The contract specifies behaviors for handling dynamic updates, prioritizing stability and correctness:
65- //
66- // - Immutable Flow Identity (`types.FlowKey`): The system treats the `FlowKey` (`ID` + `Priority`) as the immutable
67- // identifier. Changing the priority of traffic requires registering a new `FlowKey`. The old flow instance is
68- // automatically garbage collected when Idle. This design eliminates complex priority migration logic.
69- //
70- // - Graceful Draining (Shard Scale-Down): Decommissioned shards enter a Draining state. They stop accepting new
71- // requests but continue to be processed for dispatch until empty.
72- //
73- // - Self-Balancing (Shard Scale-Up): When new shards are added, the `controller.FlowController`'s distribution logic
74- // naturally utilizes them, funneling new requests to the less-loaded shards. Existing queued items are not
75- // migrated.
7656type FlowRegistryAdmin interface {
77- // RegisterOrUpdateFlow handles the registration of a new flow instance or the update of an existing instance's
78- // specification (for the same `types.FlowKey`). The operation is atomic across all shards.
79- //
80- // Since the `FlowKey` (including `Priority`) is immutable, this method cannot change a flow's priority.
81- // To change priority, the caller should simply register the new `FlowKey`; the old flow instance will be
82- // automatically garbage collected when it becomes Idle.
83- //
84- // Returns errors wrapping `ErrFlowIDEmpty`, `ErrPriorityBandNotFound`, or internal errors if plugin instantiation
85- // fails.
86- RegisterOrUpdateFlow (spec types.FlowSpecification ) error
87-
88- // UpdateShardCount dynamically adjusts the number of internal state shards.
89- //
90- // The implementation MUST atomically re-partition capacity allocations across all active shards.
91- // Returns an error wrapping `ErrInvalidShardCount` if `n` is not positive.
92- UpdateShardCount (n int ) error
93-
9457 // Stats returns globally aggregated statistics for the entire `FlowRegistry`.
9558 Stats () AggregateStats
9659
97- // ShardStats returns a slice of statistics, one for each internal shard. This provides visibility for debugging and
98- // monitoring per-shard behavior (e.g., identifying hot or stuck shards).
60+ // ShardStats returns a slice of statistics, one for each internal shard.
9961 ShardStats () []ShardStats
10062}
10163
102- // ShardProvider defines the interface for discovering available shards.
103- //
104- // A "shard" is an internal, parallel execution unit that allows the `controller.FlowController`'s core dispatch logic
105- // to be parallelized, preventing a CPU bottleneck at high request rates. The `FlowRegistry`'s state is sharded to
106- // support this parallelism by reducing lock contention.
64+ // FlowRegistryClient defines the primary, client-facing interface for the registry.
65+ // This is the interface that the `controller.FlowController`'s data path depends upon.
66+ type FlowRegistryClient interface {
67+ // WithConnection manages a scoped, leased session for a given flow.
68+ // It is the primary and sole entry point for interacting with the data path.
69+ //
70+ // This method handles the entire lifecycle of a flow connection:
71+ // 1. Just-In-Time (JIT) Registration: If the flow for the given `types.FlowKey` does not exist, it is created and
72+ // registered automatically.
73+ // 2. Lease Acquisition: It acquires a lifecycle lease, protecting the flow from garbage collection.
74+ // 3. Callback Execution: It invokes the provided function `fn`, passing in a temporary `ActiveFlowConnection` handle.
75+ // 4. Guaranteed Lease Release: It ensures the lease is safely released when the callback function returns.
76+ //
77+ // This functional, callback-based approach makes resource leaks impossible, as the caller is not responsible for
78+ // manually closing the connection.
79+ //
80+ // Errors returned by the callback `fn` are propagated up.
81+ // Returns `ErrFlowIDEmpty` if the provided key has an empty ID.
82+ WithConnection (key types.FlowKey , fn func (conn ActiveFlowConnection ) error ) error
83+ }
84+
85+ // ActiveFlowConnection represents a handle to a temporary, leased session on a flow.
86+ // It provides a safe, scoped entry point to the registry's sharded data plane.
10787//
108- // Consumers MUST check `RegistryShard.IsActive()` before routing new work to a shard to avoid sending requests to a
109- // Draining shard.
110- type ShardProvider interface {
111- // Shards returns a slice of accessors, one for each internal state shard (Active and Draining).
112- // Callers should not modify the returned slice.
88+ // An `ActiveFlowConnection` instance is only valid for the duration of the `WithConnection` callback from which it was
89+ // received. Callers MUST NOT store a reference to this object or use it after the callback returns.
90+ // Its purpose is to ensure that any interaction with the flow's state (e.g., accessing its shards and queues) occurs
91+ // safely while the flow is guaranteed to be protected from garbage collection.
92+ type ActiveFlowConnection interface {
93+ // Shards returns a stable snapshot of accessors for all internal state shards (both Active and Draining).
94+ // Consumers MUST check `RegistryShard.IsActive()` before routing new work to a shard from this slice.
11395 Shards () []RegistryShard
11496}
11597
116- // RegistryShard defines the interface for accessing a specific slice (shard) of the `FlowRegistry's` state.
117- // It provides a concurrent-safe view for `controller.FlowController` workers.
118- //
119- // # Conformance
98+ // RegistryShard defines the interface for a single slice (shard) of the `FlowRegistry`'s state.
99+ // A shard acts as an independent, parallel execution unit, allowing the system's dispatch logic to scale horizontally.
120100//
121- // All methods MUST be goroutine-safe.
101+ // # Conformance: Implementations MUST be goroutine-safe.
122102type RegistryShard interface {
123103 // ID returns a unique identifier for this shard, which must remain stable for the shard's lifetime.
124104 ID () string
0 commit comments