Skip to content

[Flow Control] Priority-Based Progressive Holdback (Adaptive Capacity Reserves) #1185

Description

@LukeAVanDrie

Flow control currently enforces binary backpressure: when the backend pool reaches 100% saturation, EPP stops dispatching and queues incoming requests.

To protect critical real-time latency SLOs under heavy load, we need a progressive holdback mechanism. This allows EPP to proactively gate/buffer lower-priority traffic (e.g., best-effort batch at 75% load, standard at 90% load) as pool saturation rises, thereby maintaining a reserve of available compute capacity to absorb sudden high-priority bursts.

Since priority levels are dynamically derived from InferenceObjective CRDs at runtime, hardcoded mappings are brittle. We will implement a parameter-driven priority-holdback-policy* plugin. This plugin will implement EPP's pluggable flowcontrol.UsageLimitPolicy` interface and support multiple gating algorithms to dynamically distribute admission ceilings across the $[0, 1]$ saturation range.


1. Pluggable Progressive Gating Strategies

The plugin will compute an admission ceiling $c_i \in [0.0, 1.0]$ for each active priority. In the EPP dispatchCycle, traffic is gated if the pool's current saturation exceeds the priority's ceiling ($S > c_i$).

The plugin may support the following gating strategies (seeking feedback here):

A. static

  • Algorithm: Fixed, explicit mapping.
  • Formula:
    $$c_i = \begin{cases}
    \text{StaticMap}[p_i] & \text{if } p_i \in \text{StaticMap} \
    \text{DefaultStaticCeiling} & \text{otherwise}
    \end{cases}$$

B. stepwise-spread

  • Algorithm: Automatically divides the range between a configured minCeiling ($c_{min}$) and maxCeiling ($c_{max}$) into equal steps based strictly on the number $N$ of currently active priorities, ignoring their absolute numerical difference.
  • Formula (for index $i$ in sorted descending active priorities):
    $$c_i = c_{max} - i \times \frac{c_{max} - c_{min}}{N - 1}$$

C. linear-proportional

  • Algorithm: Scales ceilings linearly between $c_{min}$ and $c_{max}$ based on the numerical value of active priorities relative to the observed active range $[p_{min}, p_{max}]$.
  • Formula:
    $$r_i = \frac{p_i - p_{min}}{p_{max} - p_{min}}$$
    $$c_i = c_{min} + r_i \times (c_{max} - c_{min})$$

D. sigmoid-smoothed

  • Algorithm: Maps priorities to ceilings along a normalized logistic sigmoid curve to create "soft floors" and "soft ceilings", grouping similar priorities together.
  • Formula:
    $$\sigma(x) = \frac{1}{1 + e^{-k(x - x_0)}}$$
    $$S_{norm}(x) = \frac{\sigma(x) - \sigma(0)}{\sigma(1) - \sigma(0)}$$
    $$c_i = c_{min} + S_{norm}(x_i) \times (c_{max} - c_{min})$$
    (Support mapping over either the absolute priority value domain or the relative index rank domain)

2. Safety Guarantees & Corner Cases

  • Single Active Priority ($N = 1$): To respect flow control's work-conserving nature, if only a single priority level is active, EPP will bypass progressive holdback and assign it $c_{max}$ (usually 1.0), preventing artificial limits when the pool has spare capacity.
  • Dynamic Priority Inversion Prevention: To guarantee that a higher priority is never gated before a lower priority (regardless of mathematical edge cases), the plugin will run a right-to-left monotonizing sweep over the calculated ceilings:
    // Ensure ceilings[i] >= ceilings[i+1] (highest first)
    for i := n - 2; i >= 0; i-- {
        if ceilings[i] < ceilings[i+1] {
            ceilings[i] = ceilings[i+1]
        }
    }
  • Boot-Time Validation: The plugin factory will perform local priority-inversion validation on startup when using the static mapping. EPP will fail to boot on invalid configurations.

3. Configuration Schema

The policy is configured inside the existing EPP plugins block and referenced inside flowControl:

apiVersion: inference.networking.x-k8s.io/v1alpha1
kind: EndpointPickerConfig
metadata:
  name: default-config
featureGates:
- flowControl
plugins:
- type: priority-holdback-policy
  name: dynamic-holdback
  parameters:
    strategy: "sigmoid-smoothed"
    minCeiling: 0.6
    maxCeiling: 1.0
    sigmoidSteepness: 8.0
    sigmoidMidpoint: 0.5
    sigmoidDomain: "value"
flowControl:
  usageLimitPolicyPluginRef: dynamic-holdback

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIndicates an issue or PR lacks a triage label and requires one.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions