Job System
Praxrr runs background work through a SQLite-backed job queue and a timer-driven
job dispatcher. Handlers register via side-effect imports; scheduling lives in
schedule.ts and is separate from sync-module trigger logic in $sync/processor.ts.
Persistence
Section titled “Persistence”Jobs are stored in the app database (job_queue table) via jobQueueQueries:
| Field | Purpose |
|---|---|
jobType |
Discriminator for handler lookup |
runAt |
ISO timestamp when the job becomes due |
payload |
Typed JSON per job type |
source |
schedule, manual, or system |
dedupeKey |
Prevents duplicate scheduled jobs for the same logical work |
cooldownUntil |
Optional cooldown passed through on reschedule |
status |
queued, running, success, failed, cancelled |
Run history is recorded in job_run_history for debugging and UI display.
Job Types
Section titled “Job Types”Defined in queueTypes.ts:
| Type | Typical payload |
|---|---|
arr.sync.qualityProfiles |
{ instanceId } |
arr.sync.delayProfiles |
{ instanceId } |
arr.sync.mediaManagement |
{ instanceId } |
arr.sync.metadataProfiles |
{ instanceId } |
arr.sync |
{ instanceId, sections? } (legacy combined) |
arr.pull.startup |
{ enqueuedAt? } |
arr.upgrade |
{ instanceId } |
arr.rename |
{ instanceId } |
pcd.sync |
{ databaseId } |
trashguide.sync |
{ sourceId, trigger } |
backup.create / backup.cleanup |
{} |
logs.cleanup |
{} |
config-health.snapshot |
{} |
config-health.cleanup |
{} |
Config Health Jobs
Section titled “Config Health Jobs”config-health.snapshot runs a chunked, instance-ordered sweep of enabled sync-capable Arr
instances. It scores each instance and appends the report to config_health_snapshots. Scheduled
sweeps persist their cursor between chunks and schedule the next interval after the terminal chunk;
a manual run does not make itself recurring.
Snapshot insertion is the primary-operation boundary. Only after the insert succeeds does the
handler read the adjacent persisted predecessor and evaluate a possible health.degraded event.
Assessment, state claim or re-arm, rendering, notification manager, provider, history, and secondary
logging failures are contained by that post-insert boundary. They do not undo the snapshot, fail
sibling instances, change sweep progress, or activate the snapshot handler’s failure backoff.
config-health.cleanup prunes snapshots by retention age and then caps the rows that remain. The
scheduled job recurs daily; a manual run performs one cleanup only. Both Config Health jobs cancel
when Config Health scoring is disabled.
Dispatcher
Section titled “Dispatcher”JobDispatcher in dispatcher.ts:
- Queries the next queued job and sets a timer for
runAt - On wake, claims due jobs in a loop (
claimNextDue) - Looks up the handler in
jobQueueRegistry - Executes the handler and records run history
- On
rescheduleAtin the handler result, reschedules instead of marking finished
There is no central retry policy — handlers return rescheduleAt and optional
cooldownUntil when they want deferred retry.
Handler Registration
Section titled “Handler Registration”Handlers self-register when imported. dispatcher.ts imports ./handlers/index.ts,
which side-effect imports:
arrSync.ts,arrPullStartup.ts,arrUpgrade.ts,arrRename.tspcdSync.ts,trashGuideSync.tsbackupCreate.ts,backupCleanup.ts,logsCleanup.ts
Adding a job type requires a handler file, registry entry, and queue type definition.
Scheduling
Section titled “Scheduling”schedule.ts functions upsert scheduled jobs with dedupe keys such as
arr.sync.qualityProfiles:{instanceId} or pcd.sync:{databaseId}. Cron-based Arr sync
schedules read sync config from arrSyncQueries and compute next run times.
scheduleAllJobs() runs during initializeJobs() after recovering interrupted
running jobs back to queued.
Startup Recovery
Section titled “Startup Recovery”initializeJobs() in init.ts:
jobQueueQueries.recoverRunning()— reset stuck running jobsscheduleAllJobs()— refresh scheduled work for instances and databasesjobDispatcher.start()— begin the claim loop
Relationship to Sync
Section titled “Relationship to Sync”Sync execution is triggered by arr.sync.* jobs and by direct calls from
triggerSyncs() after PCD pulls or changes. Sync scheduling in $sync/ evaluates
cron triggers and enqueues jobs — do not conflate that module with jobs/schedule.ts
persistence mechanics.
Source References
Section titled “Source References”packages/praxrr-app/src/lib/server/jobs/dispatcher.tspackages/praxrr-app/src/lib/server/jobs/init.tspackages/praxrr-app/src/lib/server/jobs/schedule.tspackages/praxrr-app/src/lib/server/jobs/queueTypes.tspackages/praxrr-app/src/lib/server/jobs/handlers/packages/praxrr-app/src/lib/server/jobs/handlers/configHealthSnapshot.tspackages/praxrr-app/src/lib/server/jobs/handlers/configHealthCleanup.ts
Related
Section titled “Related”- Sync Pipeline — preview vs execution,
arr.sync.*jobs - Startup Sequence —
initializeJobs()during boot - Architecture Overview — module map
- Testing —
deno task test jobsalias