0.2.79 - Slow api fixes
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// The whole point of the claim is that it is true exactly once. A progress report arrives
|
||||
// every ten seconds for the length of an episode and its own guard — half watched — is
|
||||
// true for every one of those after the halfway mark, so without this the check behind it
|
||||
// ran two Emby lookups, a Sonarr catalogue read and a Postgres write a hundred times per
|
||||
// episode and discarded all but the first.
|
||||
func TestFollowCheckIsClaimedOnce(t *testing.T) {
|
||||
var checks followChecks
|
||||
if !checks.claim("user-1", "episode-1") {
|
||||
t.Fatal("the first report should claim the check")
|
||||
}
|
||||
for range 10 {
|
||||
if checks.claim("user-1", "episode-1") {
|
||||
t.Fatal("a repeat report must not claim the check again")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Two people watching the same episode on two televisions are two viewers, and each of
|
||||
// them has their own My Shows list.
|
||||
func TestFollowCheckIsPerViewerAndPerEpisode(t *testing.T) {
|
||||
var checks followChecks
|
||||
checks.claim("user-1", "episode-1")
|
||||
if !checks.claim("user-2", "episode-1") {
|
||||
t.Fatal("another viewer must get their own check")
|
||||
}
|
||||
if !checks.claim("user-1", "episode-2") {
|
||||
t.Fatal("the next episode must get its own check")
|
||||
}
|
||||
}
|
||||
|
||||
// An empty id is not a claim to record. The alternative is one entry that swallows the
|
||||
// first real check for whichever viewer or episode arrives unnamed.
|
||||
func TestFollowCheckRefusesEmptyIdentifiers(t *testing.T) {
|
||||
var checks followChecks
|
||||
if checks.claim("", "episode-1") || checks.claim("user-1", "") {
|
||||
t.Fatal("an unnamed viewer or episode is not a check to claim")
|
||||
}
|
||||
}
|
||||
|
||||
// The memory is bounded, and what falls out of it falls out oldest first. A gateway that
|
||||
// has been up for a month must not be holding every episode the house has ever watched.
|
||||
func TestFollowCheckForgetsTheOldestFirst(t *testing.T) {
|
||||
var checks followChecks
|
||||
for index := range consideredFollows + 1 {
|
||||
checks.claim("user-1", fmt.Sprintf("episode-%d", index))
|
||||
}
|
||||
if !checks.claim("user-1", "episode-0") {
|
||||
t.Fatal("the oldest entry should have been evicted")
|
||||
}
|
||||
if checks.claim("user-1", fmt.Sprintf("episode-%d", consideredFollows)) {
|
||||
t.Fatal("the newest entry should still be held")
|
||||
}
|
||||
}
|
||||
|
||||
// Checking and recording are one call because two would be a race, and the thing being
|
||||
// protected is precisely a burst of concurrent reports for one playback.
|
||||
func TestFollowCheckClaimsOnceUnderConcurrency(t *testing.T) {
|
||||
var (
|
||||
checks followChecks
|
||||
claimed int
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
for range 32 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if checks.claim("user-1", "episode-1") {
|
||||
mu.Lock()
|
||||
claimed++
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if claimed != 1 {
|
||||
t.Fatalf("claimed %d times, want exactly 1", claimed)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user