76 lines
1.9 KiB
Go
76 lines
1.9 KiB
Go
package usage
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/yetone/magpie/internal/sessions"
|
|
)
|
|
|
|
// The sources a query parses are read several at a time, each handed back
|
|
// as its own, in any order asked; never more than a window of them waits to
|
|
// be taken, and one not wanted is read when it is asked for.
|
|
func TestPrefetchSources(t *testing.T) {
|
|
var sources []sessions.CallSource
|
|
for i := range 200 {
|
|
sources = append(sources, sessions.CallSource{Path: fmt.Sprint(i)})
|
|
}
|
|
var mu sync.Mutex
|
|
reads := map[string]int{}
|
|
var busy, most atomic.Int32
|
|
readSource := func(s sessions.CallSource) []sessions.Call {
|
|
n := busy.Add(1)
|
|
for {
|
|
m := most.Load()
|
|
if n <= m || most.CompareAndSwap(m, n) {
|
|
break
|
|
}
|
|
}
|
|
time.Sleep(time.Millisecond)
|
|
busy.Add(-1)
|
|
mu.Lock()
|
|
reads[s.Path]++
|
|
mu.Unlock()
|
|
return []sessions.Call{{Model: s.Path}}
|
|
}
|
|
want := func(s sessions.CallSource) bool { return s.Path != "7" }
|
|
read := prefetchSources(sources, want, readSource)
|
|
for i := range sources {
|
|
if i == 50 {
|
|
// nothing is taken for a while: the readers stop at the window
|
|
time.Sleep(50 * time.Millisecond)
|
|
mu.Lock()
|
|
ahead := len(reads)
|
|
mu.Unlock()
|
|
if ahead > 50+32+8 {
|
|
t.Fatalf("%d read with 50 taken", ahead)
|
|
}
|
|
}
|
|
cs := read(i)
|
|
if len(cs) != 1 || cs[0].Model != sources[i].Path {
|
|
t.Fatalf("source %d: %+v", i, cs)
|
|
}
|
|
}
|
|
if most.Load() < 2 {
|
|
t.Fatalf("at most %d read at once", most.Load())
|
|
}
|
|
for _, s := range sources {
|
|
if reads[s.Path] != 1 {
|
|
t.Fatalf("%s read %d times", s.Path, reads[s.Path])
|
|
}
|
|
}
|
|
|
|
// one or none wanted: read as asked, nothing ahead
|
|
reads = map[string]int{}
|
|
read = prefetchSources(sources[:3], func(s sessions.CallSource) bool { return s.Path == "1" }, readSource)
|
|
if len(reads) != 0 {
|
|
t.Fatalf("read ahead: %v", reads)
|
|
}
|
|
if cs := read(2); cs[0].Model != "2" {
|
|
t.Fatalf("%+v", cs)
|
|
}
|
|
}
|