Skip to content

Commit 4d605c3

Browse files
committed
fix(epp): clamp encoder matched size to input size
The predictor rejects matched > input, so inconsistent match data or mismatched feature slices would fail validation and drop the whole prediction or training entry instead of degrading gracefully. Signed-off-by: Kaushik Mitra <kaushikmitra@google.com>
1 parent b4bf2ac commit 4d605c3

4 files changed

Lines changed: 61 additions & 1 deletion

File tree

pkg/epp/framework/plugins/requestcontrol/dataproducer/predictedlatency/dataproducer_hooks.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ func (pl *PredictedLatency) captureEncoderCacheSizes(ctx context.Context, predic
136136
}
137137
inputSize := sumItemSizes(matchInfo.RequestItems())
138138
matchedSize := sumItemSizes(matchInfo.MatchedItems())
139+
// The predictor rejects matched > input, so clamp inconsistent match data
140+
// rather than dropping the whole prediction or training entry.
141+
if matchedSize > inputSize {
142+
logger.V(logutil.DEBUG).Info("Encoder cache matched size exceeds input size, clamping",
143+
"pod", endpoint.GetMetadata().String(),
144+
"encoderInputSize", inputSize,
145+
"encoderMatchedSize", matchedSize)
146+
matchedSize = inputSize
147+
}
139148
predictedLatencyCtx.encoderInputSize = inputSize
140149
predictedLatencyCtx.encoderMatchedSizeForEndpoints[endpoint.GetMetadata().NamespacedName.Name] = matchedSize
141150
logger.V(logutil.DEBUG).Info("Encoder cache sizes for pod",

pkg/epp/framework/plugins/requestcontrol/dataproducer/predictedlatency/dataproducer_hooks_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,30 @@ func TestProduce_CapturesEncoderCacheSizes(t *testing.T) {
7878
assert.Equal(t, 0, plCtx.encoderMatchedSizeForEndpoints["pod-unmatched"])
7979
}
8080

81+
// TestProduce_ClampsInconsistentEncoderMatchData verifies that match data
82+
// whose matched size exceeds the input size is clamped so downstream
83+
// predictor validation (matched <= input) cannot reject the request.
84+
func TestProduce_ClampsInconsistentEncoderMatchData(t *testing.T) {
85+
cfg := DefaultConfig
86+
cfg.PredictInProduce = false
87+
cfg.UseEncoderCacheFeatures = true
88+
pl := NewPredictedLatency(LatencyDataProviderPluginType, cfg, nil)
89+
90+
request := createTestInferenceRequest("encoder-clamp-test", 0, 0)
91+
endpoint := createTestEndpoint("pod-a", 0.1, 0, 0)
92+
93+
matched := []attrmm.MatchItem{{Hash: "img-a", Size: 3}}
94+
requestItems := []attrmm.MatchItem{{Hash: "img-b", Size: 1}}
95+
endpoint.Put(pl.encoderCacheDataKey.String(), attrmm.NewEncoderCacheMatchInfo(matched, requestItems))
96+
97+
require.NoError(t, pl.Produce(context.Background(), request, []fwksched.Endpoint{endpoint}))
98+
99+
plCtx, err := pl.getPredictedLatencyContextForRequest(request)
100+
require.NoError(t, err)
101+
assert.Equal(t, 1, plCtx.encoderInputSize)
102+
assert.Equal(t, 1, plCtx.encoderMatchedSizeForEndpoints["pod-a"])
103+
}
104+
81105
// TestProduce_EncoderCacheFeatureDisabledIgnoresMatchData is the negative
82106
// control: with the feature off, attached match data is not read.
83107
func TestProduce_EncoderCacheFeatureDisabledIgnoresMatchData(t *testing.T) {

pkg/epp/framework/plugins/requestcontrol/dataproducer/predictedlatency/training.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ func bulkPredictWithMetrics(
227227
bulkRequests[i].EncoderInputSize = encoderInputSizes[i]
228228
}
229229
if i < len(encoderMatchedSizes) {
230-
bulkRequests[i].EncoderMatchedSize = encoderMatchedSizes[i]
230+
// The predictor rejects matched > input, so clamp instead of
231+
// failing the whole bulk request when the slices are inconsistent.
232+
bulkRequests[i].EncoderMatchedSize = min(encoderMatchedSizes[i], bulkRequests[i].EncoderInputSize)
231233
}
232234
}
233235

pkg/epp/framework/plugins/requestcontrol/dataproducer/predictedlatency/training_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,31 @@ func TestBulkPredictWithMetrics_PropagatesEncoderSizes(t *testing.T) {
142142
assert.Equal(t, 0, mockPredictor.capturedBulkStrictRequests[1].EncoderMatchedSize)
143143
}
144144

145+
// TestBulkPredictWithMetrics_ClampsMatchedWithoutInputSizes verifies that a
146+
// matched-size slice without a corresponding input-size slice is clamped to
147+
// the input size (0) instead of producing requests that fail validation.
148+
func TestBulkPredictWithMetrics_ClampsMatchedWithoutInputSizes(t *testing.T) {
149+
mockPredictor := &mockPredictor{
150+
predictions: map[string]*latencypredictor.PredictionResponse{
151+
"0.5": {TTFT: 0.5, TPOT: 0.03},
152+
},
153+
}
154+
155+
metricsStates := []*fwkdl.Metrics{{KVCacheUsagePercent: 0.5}}
156+
pods := []*fwkdl.EndpointMetadata{
157+
{NamespacedName: types.NamespacedName{Namespace: "default", Name: "pod1"}},
158+
}
159+
160+
_, err := bulkPredictWithMetrics(context.Background(), "test-plugin", "test-type", nil, mockPredictor,
161+
metricsStates, "", pods, []int{1}, []int{1}, []float64{0.0},
162+
nil, nil, nil, []int{5})
163+
require.NoError(t, err)
164+
165+
require.Len(t, mockPredictor.capturedBulkStrictRequests, 1)
166+
assert.Equal(t, 0, mockPredictor.capturedBulkStrictRequests[0].EncoderInputSize)
167+
assert.Equal(t, 0, mockPredictor.capturedBulkStrictRequests[0].EncoderMatchedSize)
168+
}
169+
145170
func TestBuildPredictionRequestAndTrainingEntry_EncoderSizes(t *testing.T) {
146171
m := &fwkdl.Metrics{KVCacheUsagePercent: 0.5}
147172
pod := &fwkdl.EndpointMetadata{NamespacedName: types.NamespacedName{Namespace: "default", Name: "pod1"}}

0 commit comments

Comments
 (0)