Merge remote-tracking branch 'origin/master' into v2
This commit is contained in:
@@ -32,10 +32,10 @@ func TestParallelAndQueue(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
rel, waited, err := l.Acquire(ctx, "alpha", "m")
|
rel, waited, err := l.Acquire(ctx, "alpha", "m")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer rel()
|
|
||||||
if waited < 40*time.Millisecond {
|
if waited < 40*time.Millisecond {
|
||||||
err = errors.New("third acquire did not wait")
|
err = errors.New("third acquire did not wait")
|
||||||
}
|
}
|
||||||
|
rel() // release before reporting, so the final count check cannot race it
|
||||||
}
|
}
|
||||||
got3 <- err
|
got3 <- err
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -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) {
|
func TestQueueFullIs503(t *testing.T) {
|
||||||
alpha := newUpstream(t, "alpha")
|
alpha := newUpstream(t, "alpha")
|
||||||
alpha.delay = 400 * time.Millisecond
|
alpha.delay = 400 * time.Millisecond
|
||||||
@@ -99,14 +112,20 @@ hosts = ["alpha"]
|
|||||||
default_model = "shared"
|
default_model = "shared"
|
||||||
`, alpha)
|
`, alpha)
|
||||||
codes := make(chan int, 3)
|
codes := make(chan int, 3)
|
||||||
for i := 1; i <= 3; i++ {
|
fire := func(i int) {
|
||||||
go func(i int) {
|
go func() {
|
||||||
resp := r.post("/r/v1/chat/completions", conversation(i, 1))
|
resp := r.post("/r/v1/chat/completions", conversation(i, 1))
|
||||||
drain(resp)
|
drain(resp)
|
||||||
codes <- resp.StatusCode
|
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{}
|
got := map[int]int{}
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
got[<-codes]++
|
got[<-codes]++
|
||||||
|
|||||||
@@ -82,3 +82,17 @@ reason.
|
|||||||
per argv element, so check 3 could never pass. Test fault (mine): the body now goes through a
|
per argv element, so check 3 could never pass. Test fault (mine): the body now goes through a
|
||||||
file (`-d @file`). Ornith diagnosed it correctly. Resumed from the working tree with the
|
file (`-d @file`). Ornith diagnosed it correctly. Resumed from the working tree with the
|
||||||
corrected script.
|
corrected script.
|
||||||
|
- 2026-09-25, task 05, third session: `TestParallelAndQueue` (v1 given `limiter_test.go`) failed
|
||||||
|
once under full-suite `-race` load with `after releases: inflight 1 queued 0`. Test fault
|
||||||
|
(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.
|
||||||
|
|||||||
Reference in New Issue
Block a user