92 lines
2.6 KiB
Go
92 lines
2.6 KiB
Go
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)
|
|
}
|
|
}
|