Skip to content

Commit ecc544e

Browse files
committed
Update config to use shape and domain instead of strategy
Signed-off-by: Loic Marchal <lmarchal@redhat.com>
1 parent 12975bf commit ecc544e

4 files changed

Lines changed: 177 additions & 126 deletions

File tree

pkg/epp/framework/plugins/flowcontrol/usagelimits/priorityholdback/README.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,29 @@ Each active priority level receives an admission ceiling in [0.0, 1.0]. During e
1212

1313
Higher-priority traffic continues to flow until saturation reaches its own (higher) ceiling, providing quality-of-service differentiation under load.
1414

15-
## Strategies
15+
## Configuration
1616

17-
Two strategies are available for computing per-priority ceilings. Both produce `maxCeiling` for the highest priority and `minCeiling` for the lowest.
17+
Behavior is configured via two independent parameters: `shape` and `domain`. The resulting ceilings always range from `maxCeiling` for the highest priority to `minCeiling` for the lowest.
1818

19-
### `stepwise-spread`
19+
### `shape`
2020

21-
Distributes ceilings in equal steps by rank, ignoring numerical priority values.
21+
The interpolation curve used to distribute ceilings across the range. Currently only `"linear"` is supported.
22+
23+
### `domain`
24+
25+
How priority levels are mapped to positions in the ceiling range.
26+
27+
#### `rank` (default)
28+
29+
Distributes ceilings in equal steps by ordinal rank, ignoring numerical priority values. Ceilings are evenly spaced across `[minCeiling, maxCeiling]`.
2230

2331
c_i = maxCeiling - i * (maxCeiling - minCeiling) / (N - 1)
2432

2533
Where `i` is the index in descending priority order (0 = highest) and `N` is the count of active priorities.
2634

2735
Use when priorities represent ordinal categories (e.g., "critical", "normal", "batch") where the numerical values are arbitrary labels.
2836

29-
### `linear-proportional`
37+
#### `value`
3038

3139
Scales ceilings proportionally to the numerical priority value within the observed active range.
3240

@@ -37,7 +45,8 @@ Use when the numerical spacing between priority values carries meaning and prior
3745

3846
**Parameters:**
3947

40-
- `strategy` (string, required, no default): Gating algorithm: `"stepwise-spread"` or `"linear-proportional"`.
48+
- `shape` (string, optional, default: `"linear"`): Interpolation curve. Currently only `"linear"` is supported.
49+
- `domain` (string, optional, default: `"rank"`): Priority mapping: `"rank"` or `"value"`.
4150
- `minCeiling` (float64, required, no default): Ceiling for the lowest priority. Must be in `[0.0, 1.0)`.
4251
- `maxCeiling` (float64, optional, default: `1.0`): Ceiling for the highest priority. Must be in `(0.0, 1.0]`.
4352

@@ -49,7 +58,8 @@ plugins:
4958
- type: priority-holdback-policy
5059
name: my-holdback-policy
5160
parameters:
52-
strategy: stepwise-spread
61+
shape: linear
62+
domain: rank
5363
minCeiling: 0.4
5464
maxCeiling: 0.9
5565
flowControl:

pkg/epp/framework/plugins/flowcontrol/usagelimits/priorityholdback/config.go

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,40 @@ import (
2323
"k8s.io/utils/ptr"
2424
)
2525

26-
// strategy constants define the supported gating strategies.
26+
// shape constants define the interpolation curve applied across the ceiling range.
27+
// Only "linear" is supported; additional shapes (sigmoid, exponential, step, etc.) may be added.
2728
const (
28-
// strategyStepwiseSpread divides the [minCeiling, maxCeiling] range into equal steps based on
29-
// the count of active priorities, ignoring their absolute numerical difference.
30-
strategyStepwiseSpread = "stepwise-spread"
29+
shapeLinear = "linear"
30+
)
3131

32-
// strategyLinearProportional scales ceilings linearly based on numerical priority values
33-
// relative to the observed active range.
34-
strategyLinearProportional = "linear-proportional"
32+
// domain constants define how priority levels are mapped to positions in the ceiling range.
33+
const (
34+
// domainRank maps by ordinal rank, ignoring numerical priority values.
35+
domainRank = "rank"
36+
// domainValue maps proportionally to numerical priority values.
37+
domainValue = "value"
3538
)
3639

3740
const (
38-
// defaultMaxCeiling allows the highest priority to use full capacity.
41+
defaultShape = shapeLinear
42+
defaultDomain = domainRank
3943
defaultMaxCeiling float64 = 1.0
4044
)
4145

4246
// apiConfig represents the external configuration schema for the priority holdback policy.
4347
// It is designed to be deserialized from JSON via the plugin's raw parameters.
4448
type apiConfig struct {
45-
// Strategy selects the gating algorithm used to compute per-priority admission ceilings.
49+
// Shape selects the interpolation curve used to distribute ceilings across the range.
50+
//
51+
// Optional, defaults to "linear". Currently only "linear" is supported.
52+
Shape *string `json:"shape,omitempty"`
53+
54+
// Domain selects how priority levels are mapped to positions in the ceiling range.
55+
// - "rank": equal spacing by ordinal rank, ignoring numerical values.
56+
// - "value": spacing proportional to numerical priority differences.
4657
//
47-
// Required. Valid values: "stepwise-spread", "linear-proportional".
48-
Strategy *string `json:"strategy"`
58+
// Optional, defaults to "rank".
59+
Domain *string `json:"domain,omitempty"`
4960

5061
// MinCeiling is the admission ceiling assigned to the lowest-priority traffic.
5162
// Determines how aggressively the lowest priority is gated as saturation rises.
@@ -62,7 +73,8 @@ type apiConfig struct {
6273

6374
// config is the internal, fully-validated configuration used by the policy.
6475
type config struct {
65-
strategy string
76+
shape string
77+
domain string
6678
minCeiling float64
6779
maxCeiling float64
6880
}
@@ -87,28 +99,29 @@ func buildConfig(apiCfg *apiConfig) (*config, error) {
8799
}
88100

89101
return &config{
90-
strategy: *safeCfg.Strategy,
102+
shape: *safeCfg.Shape,
103+
domain: *safeCfg.Domain,
91104
minCeiling: *safeCfg.MinCeiling,
92105
maxCeiling: *safeCfg.MaxCeiling,
93106
}, nil
94107
}
95108

96109
// checkRequired verifies that mandatory fields are present before defaulting.
97110
func checkRequired(cfg *apiConfig) error {
98-
var errs []error
99-
100-
if cfg.Strategy == nil {
101-
errs = append(errs, fmt.Errorf("strategy is required"))
102-
}
103111
if cfg.MinCeiling == nil {
104-
errs = append(errs, fmt.Errorf("minCeiling is required"))
112+
return fmt.Errorf("minCeiling is required")
105113
}
106-
107-
return errors.Join(errs...)
114+
return nil
108115
}
109116

110117
// applyDefaults populates unset optional fields with their standard defaults.
111118
func applyDefaults(cfg *apiConfig) {
119+
if cfg.Shape == nil {
120+
cfg.Shape = ptr.To(defaultShape)
121+
}
122+
if cfg.Domain == nil {
123+
cfg.Domain = ptr.To(defaultDomain)
124+
}
112125
if cfg.MaxCeiling == nil {
113126
cfg.MaxCeiling = ptr.To(defaultMaxCeiling)
114127
}
@@ -119,12 +132,21 @@ func applyDefaults(cfg *apiConfig) {
119132
func validateConfig(cfg *apiConfig) error {
120133
var errs []error
121134

122-
if cfg.Strategy != nil {
123-
switch *cfg.Strategy {
124-
case strategyStepwiseSpread, strategyLinearProportional:
135+
if cfg.Shape != nil {
136+
switch *cfg.Shape {
137+
case shapeLinear:
138+
default:
139+
errs = append(errs, fmt.Errorf("unsupported shape %q, must be %q",
140+
*cfg.Shape, shapeLinear))
141+
}
142+
}
143+
144+
if cfg.Domain != nil {
145+
switch *cfg.Domain {
146+
case domainRank, domainValue:
125147
default:
126-
errs = append(errs, fmt.Errorf("unsupported strategy %q, must be one of: %q, %q",
127-
*cfg.Strategy, strategyStepwiseSpread, strategyLinearProportional))
148+
errs = append(errs, fmt.Errorf("unsupported domain %q, must be one of: %q, %q",
149+
*cfg.Domain, domainRank, domainValue))
128150
}
129151
}
130152

pkg/epp/framework/plugins/flowcontrol/usagelimits/priorityholdback/priority_holdback.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// ceilings per priority level. Lower-priority traffic is gated at lower saturation thresholds,
33
// reserving capacity for higher-priority work as the pool approaches saturation.
44
//
5-
// Two strategies are supported:
6-
// - "stepwise-spread": equal ceiling spacing by rank, ignoring numerical priority values.
7-
// - "linear-proportional": ceiling spacing proportional to numerical priority differences.
5+
// Behavior is configured via two independent parameters:
6+
// - shape: the interpolation curve (currently "linear"; future: sigmoid, exponential, etc.).
7+
// - domain: how priorities map to positions ("rank" for ordinal, "value" for proportional).
88
package priorityholdback
99

1010
import (
@@ -47,10 +47,10 @@ var _ flowcontrol.UsageLimitPolicy = &priorityHoldbackPolicy{}
4747

4848
func newPriorityHoldbackPolicy(cfg config) *priorityHoldbackPolicy {
4949
var fn func(cMin, cMax float64, priorities []int) (ceilings []float64)
50-
switch cfg.strategy {
51-
case strategyStepwiseSpread:
50+
switch cfg.domain {
51+
case domainRank:
5252
fn = computeLimitStepwiseSpread
53-
case strategyLinearProportional:
53+
case domainValue:
5454
fn = computeLimitLinearProportional
5555
}
5656
return &priorityHoldbackPolicy{

0 commit comments

Comments
 (0)