0.2.64 update
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package credits
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Whether the server is too busy to be reading media speculatively.
|
||||
//
|
||||
// Deliberately not a telemetry subsystem. The gateway already knows the only thing that
|
||||
// actually matters here — how many televisions are playing something right now — because
|
||||
// every one of them reports playback started, progress and stopped. A scan competes with
|
||||
// those streams for the same disk and the same Emby, so the count is a better predictor of
|
||||
// harm than a CPU figure would be, and it costs nothing to keep.
|
||||
|
||||
const (
|
||||
// busyPlaybacks is how many concurrent streams make speculative scanning unwelcome. Two
|
||||
// televisions is an ordinary evening in a household and the NAS copes; a third is the
|
||||
// point at which adding ranged reads of a fourth file stops being free.
|
||||
busyPlaybacks = 2
|
||||
|
||||
// playbackStale is how long a playback is believed without a progress report. Reports
|
||||
// arrive every ten seconds, so a minute of silence means the television went away
|
||||
// without saying so — a crash, a power cut, a network drop. Without this the gauge would
|
||||
// latch busy for ever on one lost stop report and speculative scanning would never run
|
||||
// again until the container restarted.
|
||||
playbackStale = 90 * time.Second
|
||||
)
|
||||
|
||||
// PlaybackLoad counts what is playing, from the reports televisions already send.
|
||||
type PlaybackLoad struct {
|
||||
mu sync.Mutex
|
||||
active map[string]time.Time
|
||||
}
|
||||
|
||||
func NewPlaybackLoad() *PlaybackLoad {
|
||||
return &PlaybackLoad{active: map[string]time.Time{}}
|
||||
}
|
||||
|
||||
// Playing records that a stream is alive. Called from playback started and from each
|
||||
// progress report, which is what makes the staleness sweep work: a live stream keeps
|
||||
// refreshing its own timestamp.
|
||||
func (l *PlaybackLoad) Playing(sessionKey string) {
|
||||
if l == nil || sessionKey == "" {
|
||||
return
|
||||
}
|
||||
l.mu.Lock()
|
||||
l.active[sessionKey] = time.Now()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Stopped records that a stream ended.
|
||||
func (l *PlaybackLoad) Stopped(sessionKey string) {
|
||||
if l == nil || sessionKey == "" {
|
||||
return
|
||||
}
|
||||
l.mu.Lock()
|
||||
delete(l.active, sessionKey)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Busy is the one question the scheduler asks.
|
||||
func (l *PlaybackLoad) Busy(context.Context) bool {
|
||||
return l.Count() >= busyPlaybacks
|
||||
}
|
||||
|
||||
// Count is the live stream count, sweeping anything that stopped reporting. The sweep is
|
||||
// here rather than on a ticker because this is the only reader, and a background goroutine
|
||||
// to expire at most a handful of map entries would cost more than it tidies.
|
||||
func (l *PlaybackLoad) Count() int {
|
||||
if l == nil {
|
||||
return 0
|
||||
}
|
||||
cutoff := time.Now().Add(-playbackStale)
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
for key, seen := range l.active {
|
||||
if seen.Before(cutoff) {
|
||||
delete(l.active, key)
|
||||
}
|
||||
}
|
||||
return len(l.active)
|
||||
}
|
||||
Reference in New Issue
Block a user