Routes may share one lease (affinity = "route") and skip crossbar's queue (queue = false)
route.go gains Route.Affinity/Queue with PerRoute(), Queues() and affinity validation (checkRoutes moved here; config.go calls it once). limiter.Track counts a request without holding or refusing it; a release hands the slot to a waiter only while in flight <= parallel. The proxy leases a PerRoute() route under an empty fingerprint (the row keeps the real one) and uses Track when Queues() is false. Implemented by Ornith (OpenCode); owner review removed a release-on-first-flush workaround for a race in the owner's given test (see implementer log). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package limiter_test
|
||||
|
||||
// v2.3 task 02: Track counts a request without holding or refusing it. A route with queue = false
|
||||
// leaves queueing to llama-server's own slots, but its requests are still load on the host, so the
|
||||
// routes that do queue must see them.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/crossbar/internal/limiter"
|
||||
)
|
||||
|
||||
func TestTrackNeverWaitsAndCounts(t *testing.T) {
|
||||
l := limiter.New()
|
||||
l.Configure("alpha", "m", 1, 0) // one slot, no waiting room
|
||||
|
||||
start := time.Now()
|
||||
rel1 := l.Track("alpha", "m")
|
||||
rel2 := l.Track("alpha", "m")
|
||||
rel3 := l.Track("alpha", "m")
|
||||
if d := time.Since(start); d > 50*time.Millisecond {
|
||||
t.Fatalf("Track waited %v", d)
|
||||
}
|
||||
if n := l.InFlight("alpha", "m"); n != 3 {
|
||||
t.Fatalf("in flight = %d, want 3 (Track may pass parallel)", n)
|
||||
}
|
||||
if n := l.FreeSlots("alpha"); n != 0 {
|
||||
t.Errorf("free slots = %d, want 0", n)
|
||||
}
|
||||
// A queueing request sees the host full: no waiting room, so it is refused.
|
||||
if _, _, err := l.Acquire(context.Background(), "alpha", "m"); err == nil {
|
||||
t.Error("Acquire on an over-tracked pair succeeded; want ErrQueueFull")
|
||||
}
|
||||
rel1()
|
||||
rel1() // idempotent
|
||||
rel2()
|
||||
rel3()
|
||||
if n := l.InFlight("alpha", "m"); n != 0 {
|
||||
t.Errorf("in flight after release = %d, want 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
// A waiter gets a slot only once in flight is back under parallel: releasing a tracked request
|
||||
// while the pair is still over its limit must not hand the slot on.
|
||||
func TestTrackReleaseHandsOverOnlyUnderTheLimit(t *testing.T) {
|
||||
l := limiter.New()
|
||||
l.Configure("alpha", "m", 1, 1)
|
||||
relA := l.Track("alpha", "m")
|
||||
relB := l.Track("alpha", "m") // in flight 2, parallel 1
|
||||
|
||||
got := make(chan func(), 1)
|
||||
go func() {
|
||||
rel, _, err := l.Acquire(context.Background(), "alpha", "m")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
close(got)
|
||||
return
|
||||
}
|
||||
got <- rel
|
||||
}()
|
||||
waitUntil(t, func() bool { return l.Queued("alpha", "m") == 1 })
|
||||
|
||||
relA() // in flight 1 == parallel: still no free slot
|
||||
select {
|
||||
case <-got:
|
||||
t.Fatal("waiter got a slot while in flight was still at parallel")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
if n := l.InFlight("alpha", "m"); n != 1 {
|
||||
t.Fatalf("in flight = %d after one release, want 1", n)
|
||||
}
|
||||
|
||||
relB() // now the slot is free: hand it to the waiter
|
||||
select {
|
||||
case rel := <-got:
|
||||
if rel == nil {
|
||||
t.Fatal("waiter failed")
|
||||
}
|
||||
if n := l.InFlight("alpha", "m"); n != 1 {
|
||||
t.Errorf("in flight = %d with the waiter running, want 1", n)
|
||||
}
|
||||
rel()
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("waiter never got the freed slot")
|
||||
}
|
||||
if n := l.InFlight("alpha", "m"); n != 0 {
|
||||
t.Errorf("in flight at the end = %d, want 0", n)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user