Documentation
¶
Overview ¶
Package polling provides the history-polling subsystem for Sonarr/Radarr import events and the write-through poll timestamp cache.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Deps ¶
type Deps struct {
PollCache *PollCache
Store PollerStore
Metrics PollerMetrics
Alerts activity.WarnRecorder
Events PollerEvents
StatsCache StatsCacheInvalidator
}
Deps holds all dependencies for the Poller.
type ImportResult ¶
type ImportResult struct {
Req *api.SearchRequest
Source PollSource
Label string
Targets []api.SubtitleTarget
RefreshID int
}
ImportResult holds the resolved search parameters for a single arr import event.
type LiveState ¶
type LiveState struct {
Cfg PollerCfg
Engine api.SearchEngine
Sonarr PollSonarrClient
Radarr PollRadarrClient
}
LiveState holds the hot-reloadable runtime state the poller reads each cycle.
type PollCache ¶
type PollCache struct {
// contains filtered or unexported fields
}
PollCache is a write-through cache for poll timestamps. It absorbs transient DB write failures: if the DB write fails, subsequent reads still return the in-memory value instead of re-reading a stale DB entry. On first read after startup, the cache is seeded from the DB via readFn. Uses sync.Map for lock-free reads on the hot path (2 keys, read-heavy).
A failed durable write leaves the cursor DIRTY: memory and disk disagree, and a restart would replay from the older persisted position. That state is explicit — WARN-logged with its onset, retried via RetryDirty on the poll heartbeat, gauged through the optional dirty gauge, and announced when it heals — so restart replay is an expected, explained event instead of a silent drift.
func NewPollCache ¶
func NewPollCache( readFn func(ctx context.Context, key api.PollKey) (time.Time, error), setFn func(ctx context.Context, key api.PollKey, t time.Time) error, ) *PollCache
NewPollCache creates a PollCache backed by the given read/set functions.
func (*PollCache) DirtyCount ¶ added in v0.1.148
DirtyCount returns how many cursors currently have a failing persist.
func (*PollCache) Get ¶
Get returns the cached timestamp for key, falling back to the DB on miss. Uses LoadOrStore to handle the race between concurrent first-reads atomically.
func (*PollCache) RetryDirty ¶ added in v0.1.148
RetryDirty re-attempts the durable persist of every dirty cursor using its CURRENT in-memory position. Called on the poll heartbeat so a transient write failure heals within one cycle; a no-op when everything is clean.
func (*PollCache) Set ¶
Set updates both the in-memory cache and the persistent store. The cache advances unconditionally so polling keeps working through disk trouble; a failed durable write marks the cursor dirty (see PollCache doc).
func (*PollCache) SetDirtyGauge ¶ added in v0.1.148
SetDirtyGauge installs an observer for the dirty-cursor count (e.g. a Prometheus gauge). Called with the current count on every transition.
type PollCacher ¶
type PollCacher interface {
Get(ctx context.Context, key api.PollKey) time.Time
Set(ctx context.Context, key api.PollKey, t time.Time)
}
PollCacher is the interface for poll timestamp caching. Consumers depend on this interface rather than the concrete *PollCache, enabling test doubles and alternative implementations.
type PollRadarrClient ¶ added in v0.1.106
type PollRadarrClient interface {
GetHistorySince(ctx context.Context, since time.Time, eventTypes ...arrapi.EventType) ([]arrapi.HistoryRecord, error)
GetMovieByID(ctx context.Context, id int) (arrapi.Movie, error)
ResolveExcludeTagIDs(ctx context.Context, tagNames []string, logMissing bool) map[int]struct{}
RescanMovie(ctx context.Context, movieID int) error
}
PollRadarrClient is the Radarr surface the history poller uses.
type PollSonarrClient ¶ added in v0.1.106
type PollSonarrClient interface {
GetHistorySince(ctx context.Context, since time.Time, eventTypes ...arrapi.EventType) ([]arrapi.HistoryRecord, error)
GetSeriesByID(ctx context.Context, id int) (arrapi.Series, error)
GetEpisodeByID(ctx context.Context, id int) (arrapi.Episode, error)
ResolveExcludeTagIDs(ctx context.Context, tagNames []string, logMissing bool) map[int]struct{}
RescanSeries(ctx context.Context, seriesID int) error
}
PollSonarrClient is the Sonarr surface the history poller uses: import-event polling, per-item lookups, exclude-tag resolution, and a post-import rescan.
type PollSource ¶
type PollSource string
PollSource identifies the arr system that produced an import event.
const ( PollSourceSonarr PollSource = "sonarr" PollSourceRadarr PollSource = "radarr" )
Poll source constants.
type Poller ¶
type Poller struct {
// contains filtered or unexported fields
}
Poller polls Sonarr/Radarr history APIs for new import events and processes each through the search engine.
func NewPoller ¶
NewPoller creates a Poller with the given dependencies. In unconfigured mode (server.New called without WithConfig) stateFunc may return a LiveState with a nil Cfg; we fall back to a sane default TTL so construction does not panic. Run() reads the live PollInterval per cycle and the per-entry expiry is governed by the cache TTL set here, so the first poll after configuration uses the configured interval naturally; the tag cache lifetime is the only thing tied to this initial value.
func (*Poller) PollOnce ¶
PollOnce checks both Sonarr and Radarr for new import events and enqueues what it finds for the executor; it performs NO import processing itself. Returns the number of imported-history entries observed across both arr clients (used by Run to decide whether to enter adaptive-burst mode).
func (*Poller) Run ¶
Run polls on a timer, re-reading the interval from live config after each poll so hot-reloaded interval changes take effect immediately. When PollOnce reports activity, the next interval is shortened to burstPollInterval and stays there until burstPollWindow passes idle.
Detection and execution are decoupled (P12): the timer loop only FETCHES history and enqueues batches, so new imports are observed on schedule even while a large batch is still being worked through; the single-worker executor goroutine drains the queue with the existing pacing and watermark semantics.
type PollerCfg ¶
type PollerCfg interface {
PollInterval() time.Duration
Search() api.SearchConfig
ValidatePath(ctx context.Context, path string) error
ResolveTargetsWithFallback(originalLang string, audioLangs []string) []api.SubtitleTarget
LanguageCodes() []string
}
PollerCfg is the narrow configuration interface consumed by the poller subsystem. The full api.ConfigProvider satisfies it via structural typing. Declaring it here documents the poller's actual dependency surface.
type PollerEvents ¶
PollerEvents is the narrow events interface consumed by the poller.
type PollerMetrics ¶
PollerMetrics is the narrow metrics interface consumed by the poller.
type PollerStore ¶
type PollerStore interface {
DeleteStateByPaths(ctx context.Context, paths []string) (api.CleanupResult, error)
}
PollerStore is the narrow store interface consumed by poll-import processing. Only DeleteStateByPaths is needed to clean up stale entries when a video file disappears between poll cycles.
type StateFunc ¶
type StateFunc func() *LiveState
StateFunc returns the current live state. Called each poll cycle to pick up hot-reloaded config/clients.
type StatsCacheInvalidator ¶
type StatsCacheInvalidator interface {
Invalidate()
}
StatsCacheInvalidator is the narrow interface for stats cache invalidation.