From 481ea12f4ccb85df90eb3be8f919305f7eed536b Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Fri, 25 Sep 2026 11:08:16 -0700 Subject: [PATCH] v1 proxy test: order arrivals by limiter state; note the 499-on-served-200 defect for v2.1 Co-Authored-By: Claude Fable 5.1 --- .../v1/_files/internal/proxy/proxy_test.go | 27 ++++++++++++++++--- docs/plans/v2/README.md | 9 +++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/plans/v1/_files/internal/proxy/proxy_test.go b/docs/plans/v1/_files/internal/proxy/proxy_test.go index c1bb26a..39980aa 100644 --- a/docs/plans/v1/_files/internal/proxy/proxy_test.go +++ b/docs/plans/v1/_files/internal/proxy/proxy_test.go @@ -85,6 +85,19 @@ func TestDifferentConversationsSpreadByFreeSlots(t *testing.T) { } } +// waitUntil polls cond every 5 ms for up to two seconds and fails the test if it never holds. +func waitUntil(t *testing.T, cond func() bool) { + t.Helper() + deadline := time.Now().Add(2 * time.Second) + for time.Now().Before(deadline) { + if cond() { + return + } + time.Sleep(5 * time.Millisecond) + } + t.Fatal("condition not reached within two seconds") +} + func TestQueueFullIs503(t *testing.T) { alpha := newUpstream(t, "alpha") alpha.delay = 400 * time.Millisecond @@ -99,14 +112,20 @@ hosts = ["alpha"] default_model = "shared" `, alpha) codes := make(chan int, 3) - for i := 1; i <= 3; i++ { - go func(i int) { + fire := func(i int) { + go func() { resp := r.post("/r/v1/chat/completions", conversation(i, 1)) drain(resp) codes <- resp.StatusCode - }(i) - time.Sleep(30 * time.Millisecond) // arrival order: 1 runs, 2 queues, 3 finds the queue full + }() } + // Arrival order is enforced by watching the limiter, not by sleeping: 1 runs, 2 queues, + // 3 finds the queue full. + fire(1) + waitUntil(t, func() bool { return r.lim.InFlight("alpha", "shared") == 1 }) + fire(2) + waitUntil(t, func() bool { return r.lim.Queued("alpha", "shared") == 1 }) + fire(3) got := map[int]int{} for i := 0; i < 3; i++ { got[<-codes]++ diff --git a/docs/plans/v2/README.md b/docs/plans/v2/README.md index b743259..8e5c75a 100644 --- a/docs/plans/v2/README.md +++ b/docs/plans/v2/README.md @@ -87,3 +87,12 @@ reason. (mine): the third acquirer sent its result before its deferred release ran, so the final count check could observe one slot still held. The given file now releases before reporting. Ornith found it and measured the flake rate rather than editing the protected file. +- 2026-09-25, task 05, third session: also saw `TestQueueFullIs503` fail with + `Requests:3 Errors:2`. Two causes. (1) Test fault (mine): arrival order rested on 30 ms + sleeps; the given test now waits on the limiter's in-flight and queued counts. (2) A real v1 + defect, verified by the owner with a diagnostic build (5 of 8 runs): after a forward completes, + `forward.go` checks `r.Context().Err()` and, when the client has already closed its connection, + records a served 200 as a 499 "client cancelled" error. Cancellation must be what the reverse + proxy itself observed, never a post-hoc context check. Scheduled as v2.1 task 01; not fixed in + task 05, which is wiring only. The session then ended on a refused read of `/proc/loadavg` — + the seventh refusal-ending. Model fault.