Skip to content

Commit b62ff61

Browse files
committed
fix(flowcontrol): map pre-admission TTL/cancel to eviction statuses instead of 500
A TTL expiry or client disconnect that fires before an item is admitted to a queue (buffered in the enqueue channel, blocked in SubmitOrBlock, or racing managedQueue.Add) surfaces as QueueOutcomeRejectedOther wrapping ErrTTLExpired or ErrContextCancelled, which translateFlowControlOutcome mapped to 500 Internal. The same client behavior an instant later (item queued) yields EvictedTTL or EvictedContextCancelled and a 503 with a dropped-reason header. translateFlowControlOutcome now branches on those sentinels in the RejectedOther/EvictedOther case (after the shutdown check, which takes precedence) and delegates to the corresponding eviction outcome's mapping, so the pre-admission path always follows the post-admission one, including any future changes to that mapping. The EvictedOther arm gets the same branches as defensive symmetry; no current producer emits it with these sentinels. tryDistribution finalized a ManagedQueue lookup failure for a leased flow (an internal invariant violation) as QueueOutcomeRejectedCapacity, surfacing an internal error as 429 with the Saturated dropped-reason header. It now finalizes as QueueOutcomeRejectedOther with a descriptive error, which maps to 500 Internal without a saturation header. Fixes llm-d#2097 Part of llm-d#1187 Signed-off-by: Luke Van Drie <lukevandrie@google.com>
1 parent 34478d1 commit b62ff61

4 files changed

Lines changed: 52 additions & 3 deletions

File tree

pkg/epp/flowcontrol/controller/controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,10 @@ func (fc *FlowController) tryDistribution(
355355
fc.logger.Error(err,
356356
"Invariant violation. Failed to get ManagedQueue for a leased flow.",
357357
"flowKey", conn.FlowKey())
358-
item.FinalizeWithOutcome(types.QueueOutcomeRejectedCapacity, types.ErrRejected)
358+
// This is an internal invariant violation, not a capacity condition: finalize as RejectedOther so
359+
// it surfaces as an internal error rather than as saturation backpressure.
360+
item.FinalizeWithOutcome(types.QueueOutcomeRejectedOther,
361+
fmt.Errorf("%w: failed to get ManagedQueue for leased flow: %w", types.ErrRejected, err))
359362
return item, err
360363
}
361364

pkg/epp/flowcontrol/controller/controller_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ func TestFlowController_EnqueueAndWait(t *testing.T) {
417417
outcome, err := h.fc.EnqueueAndWait(context.Background(), req)
418418
require.Error(t, err, "EnqueueAndWait must reject requests if queue doesn't exist for flow")
419419
assert.ErrorIs(t, err, types.ErrRejected, "error should wrap ErrRejected")
420-
assert.Equal(t, types.QueueOutcomeRejectedCapacity, outcome,
421-
"outcome should be QueueOutcomeRejectedCapacity when queue doesn't exist for the flow")
420+
assert.Equal(t, types.QueueOutcomeRejectedOther, outcome,
421+
"outcome should be QueueOutcomeRejectedOther when queue doesn't exist for the flow")
422422
})
423423
})
424424

pkg/epp/requestcontrol/admission.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,16 @@ func translateFlowControlOutcome(outcome types.QueueOutcome, err error) error {
245245
if errors.Is(err, types.ErrFlowControllerNotRunning) {
246246
return errcommon.Error{Code: errcommon.ServiceUnavailable, Msg: "flow controller shutting down: " + msg, Headers: map[string]string{errcommon.RequestDroppedReasonHeaderKey: string(errcommon.RequestDroppedReasonShuttingDown)}}
247247
}
248+
// A TTL expiry or client disconnect that fires before the item is admitted to a queue (e.g. while
249+
// buffered in the enqueue channel or blocked in submission) surfaces as RejectedOther/EvictedOther
250+
// rather than as a dedicated eviction outcome. These are client-caused terminations, so delegate
251+
// to the same mapping as their post-admission equivalents.
252+
if errors.Is(err, types.ErrTTLExpired) {
253+
return translateFlowControlOutcome(types.QueueOutcomeEvictedTTL, err)
254+
}
255+
if errors.Is(err, types.ErrContextCancelled) {
256+
return translateFlowControlOutcome(types.QueueOutcomeEvictedContextCancelled, err)
257+
}
248258
return errcommon.Error{Code: errcommon.Internal, Msg: "internal flow control error: " + msg}
249259
default:
250260
return errcommon.Error{Code: errcommon.Internal, Msg: "unhandled flow control outcome: " + msg}

pkg/epp/requestcontrol/admission_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package requestcontrol
1919
import (
2020
"context"
2121
"errors"
22+
"fmt"
2223
"testing"
2324

2425
"github.com/stretchr/testify/assert"
@@ -364,6 +365,41 @@ func TestTranslateFlowControlOutcome(t *testing.T) {
364365
wantCode: errcommon.ServiceUnavailable,
365366
wantReason: string(errcommon.RequestDroppedReasonShuttingDown),
366367
},
368+
{
369+
name: "pre-admission TTL rejection maps like TTL eviction",
370+
outcome: fctypes.QueueOutcomeRejectedOther,
371+
err: fmt.Errorf("%w: %w", fctypes.ErrRejected, fctypes.ErrTTLExpired),
372+
wantCode: errcommon.ServiceUnavailable,
373+
wantReason: string(errcommon.RequestDroppedReasonTTLExpired),
374+
},
375+
{
376+
name: "pre-admission cancellation rejection maps like cancellation eviction",
377+
outcome: fctypes.QueueOutcomeRejectedOther,
378+
err: fmt.Errorf("%w: %w", fctypes.ErrRejected, fctypes.ErrContextCancelled),
379+
wantCode: errcommon.ServiceUnavailable,
380+
wantReason: string(errcommon.RequestDroppedReasonContextCancelled),
381+
},
382+
{
383+
name: "other TTL eviction maps like TTL eviction",
384+
outcome: fctypes.QueueOutcomeEvictedOther,
385+
err: fmt.Errorf("%w: %w", fctypes.ErrEvicted, fctypes.ErrTTLExpired),
386+
wantCode: errcommon.ServiceUnavailable,
387+
wantReason: string(errcommon.RequestDroppedReasonTTLExpired),
388+
},
389+
{
390+
name: "other cancellation eviction maps like cancellation eviction",
391+
outcome: fctypes.QueueOutcomeEvictedOther,
392+
err: fmt.Errorf("%w: %w", fctypes.ErrEvicted, fctypes.ErrContextCancelled),
393+
wantCode: errcommon.ServiceUnavailable,
394+
wantReason: string(errcommon.RequestDroppedReasonContextCancelled),
395+
},
396+
{
397+
name: "shutdown takes precedence over TTL",
398+
outcome: fctypes.QueueOutcomeRejectedOther,
399+
err: fmt.Errorf("%w: %w", fctypes.ErrFlowControllerNotRunning, fctypes.ErrTTLExpired),
400+
wantCode: errcommon.ServiceUnavailable,
401+
wantReason: string(errcommon.RequestDroppedReasonShuttingDown),
402+
},
367403
{
368404
name: "internal error returns 500",
369405
outcome: fctypes.QueueOutcomeRejectedOther,

0 commit comments

Comments
 (0)