scheduler

package
v1.1.18 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: GPL-2.0, GPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package scheduler owns the daily deep-analysis tick and its sub-workers (recent-history replay + recently-added sweep).

Responsibilities:

  • Schedule a daily deep-analysis run at an absolute HH:MM boundary (clock-jump-safe: DST, NTP step, container start mid-minute all resolve to the next HH:MM boundary).
  • Fan out per-item work across a bounded worker pool (runtime-algorithms-p1) with a circuit breaker that aborts the pass after a threshold of consecutive per-item failures.
  • Persist the last-run marker through api.Cache so a cold restart does not double-run the analysis.

Inviolate contracts preserved (see refactor-agent-guide.md):

  • WARN slog keys ("scheduler: aborting history processing after consecutive failures", "scheduler: failed to fetch history", "scheduler: failed to fetch sections", "scheduler: deep analysis already in progress, skipping") byte-for-byte identical (inviolate item 5).
  • INFO slog keys ("scheduler enabled", "scheduled deep analysis starting", "running initial deep analysis", "deep analysis completed", "scheduler: processing recently added episode", "scheduler stopped") identical.
  • /config/cache.json schema unchanged — LastSchedulerRun reads and writes go through api.Cache and are tagged by the concrete internal/cache package (inviolate item 7).

Consumer note: scheduler depends on api.PlexReader, api.Cache, api.UserLookup, and a Syncer interface satisfied by *sync.Syncer (declared locally to keep this package testable without importing internal/sync, which would create a visible dependency direction only used in one place). In practice main.go wires the concrete *sync.Syncer through.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CacheSaver

type CacheSaver func() error

CacheSaver is the narrow persistence sink the scheduler needs: a single "flush the cache to disk" call invoked at the end of each deep-analysis tick. Deliberately separate from api.Cache (which deliberately excludes file-system concerns) so the scheduler can trigger a disk flush without the api.Cache consumers needing to know about the persistence path. *cache.Cache satisfies this via a trivial closure in the composition root.

type Config

type Config struct {
	Ignore           api.IgnoreChecker // library skip rules; nil means "never skip"
	ScheduleTime     string            // "HH:MM"
	Enable           bool              // scheduler on/off
	LanguageProfiles bool              // reserved for future use
}

Config captures the subset of application configuration the Scheduler actually reads. Decoupling from the full main.config keeps the package boundary clean and lets tests construct a Scheduler without mimicking the app's full env-var surface.

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler owns the deep-analysis tick and its workers. Safe for concurrent Run calls, but the intended shape is a single Run goroutine per process.

runtime-concurrency-p1: overlapping deep-analysis triggers (e.g. the initial catch-up firing close to the first scheduled HH:MM tick) collapse onto a single in-flight run via singleflight.Group. The runner goroutine that loses the dedup race still logs a WARN with the "scheduler: deep analysis already in progress, skipping" key so Loki alerts keyed on that string continue to fire (inviolate item 5).

func New

func New(cfg Config, reader api.PlexReader, c api.Cache, lookup api.UserLookup, userClient api.UserClientFunc, s Syncer, saveCache CacheSaver) *Scheduler

New constructs a Scheduler with the given collaborators. saveCache may be nil in tests that don't exercise the disk-flush path.

func (*Scheduler) Run

func (s *Scheduler) Run(ctx context.Context)

Run is the outer scheduler loop: it runs the first deep-analysis pass if the cache's LastSchedulerRun marker is absent or older than 24h, then sleeps until the next HH:MM boundary. Returns when the context is cancelled.

type Syncer

type Syncer interface {
	ChangeTracksForEpisode(ctx context.Context, userClient api.PlexReadWriter, userID string, reference *streams.Episode, trigger string)
	ProcessNewOrUpdatedEpisodeAllUsers(ctx context.Context, episode *streams.Episode, trigger string)
}

Syncer is the narrow interface the scheduler needs from the sync package: a per-user track-apply call plus the multi-user fan-out. The ignore-library/ignore-label skip checks live on api.IgnoreChecker (injected via Config.Ignore) rather than on the Syncer, so overlapping event/scheduler paths share one decision point instead of the three duplicated implementations that existed pre-extraction. *sync.Syncer satisfies this. Declared here (rather than imported) to keep scheduler independent of internal/sync for test ergonomics.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL