Each visit re-reads the few session files agents appended to since the last read, which takes milliseconds. But refresh() then saved the whole ~20 MB ~/.cache/magpie/sessions.json before clearing the indexing flag, and that save took over a second, so /api/sessions/progress said "indexing" past the 0.7s threshold and the hero played every time. The flag now drops as soon as the parse ends; the index is written from a snapshot (parses are never changed in place, so a shallow clone is safe) by one background writer that coalesces saves. Reset and the CLI's exit wait for the write. Tested: TestSaveBehindTheRead holds the save open and requires List to return and Indexing to be off meanwhile, then the file to be written (fails with the synchronous save). Against a copy of the real index (196 sessions) the flag stayed on at most 22ms per visit. go test -race ./internal/sessions, go test -tags nogui ./... (TestAddSecondOfPreset flaky, passes alone), go build -tags nogui (and GOOS=linux, GOOS=windows), go build ./..., and the GUI tests sessions-overview, old-webkit, menu-scroll, click-scroll pass. Not tried in the real Wails window.
55 lines
1.3 KiB
Go
55 lines
1.3 KiB
Go
package sessions
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Writing the whole index to disk takes a second or more for a long
|
|
// history; a read of a few changed files neither waits on it nor counts it
|
|
// as indexing, or the page shows its indexing show for every catch-up read
|
|
// (Image #15: the show came back again and again at 3 of 3 files read).
|
|
func TestSaveBehindTheRead(t *testing.T) {
|
|
setup(t)
|
|
release := make(chan struct{})
|
|
writing := make(chan struct{}, 8)
|
|
var once sync.Once
|
|
let := func() { once.Do(func() { close(release) }) }
|
|
saveHook = func() { writing <- struct{}{}; <-release }
|
|
t.Cleanup(func() { let(); saveHook = nil })
|
|
|
|
done := make(chan []Session)
|
|
go func() { done <- List(0) }()
|
|
select {
|
|
case <-writing:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("no save started")
|
|
}
|
|
var list []Session
|
|
select {
|
|
case list = <-done:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("the read waited on the save")
|
|
}
|
|
if len(list) == 0 {
|
|
t.Fatal("no sessions read")
|
|
}
|
|
if Indexing().Indexing {
|
|
t.Fatal("still indexing while the index is saved")
|
|
}
|
|
// the save lands once let go, and a new load waits for it
|
|
let()
|
|
Saved()
|
|
b, err := os.ReadFile(CachePath())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var kept cacheFile
|
|
if json.Unmarshal(b, &kept) != nil || len(kept.Files) == 0 {
|
|
t.Fatalf("cache file: %.200s", b)
|
|
}
|
|
}
|