# Async Job Execution ```mermaid flowchart TB subgraph Caller[Job Enqueue Callers] CLI["CLI Commands
dominus run --sync
dominus run --wait"] API["REST API
POST /api/v1/runs
POST /api/v1/portfolio/rescore"] SCHED[Scheduler
Cron: backup, prune,
watchlist, renewal check] end subgraph Enqueue[Enqueue Path] E["enqueueRun() / enqueueJob()
INSERT INTO job_queue"] JQ[("Job Queue
(SQLite / PostgreSQL
job_queue table)")] end subgraph Worker[Job Worker] direction TB POLL[Poll Loop
configurable 5s interval
adaptive backoff on SQLITE_BUSY] CLAIM["Claim Jobs
UPDATE status = 'running'
WHERE status = 'pending'
LIMIT concurrency"] DISPATCH[Handler Dispatch
Map<JobType, JobHandler>] ABORT[Abort Controller
per active job
signal-aware handlers] end subgraph Handlers[Registered Job Handlers] PRH[PipelineRunHandler
5-stage pipeline execution] PRH2[PortfolioRescoreHandler
rescore all portfolio domains] BTH[BacktestBuildHandler
run backtest simulations] BKH[BackupHandler
database backup to disk] PRN[PruneHandler
expired cache/data cleanup] WTH[WatchlistPollHandler
poll watched domains] RNW[RenewalCheckHandler
check renewal dates] WTT[WeightTuneHandler
ML-free weight tuning] end subgraph Results[Results & Monitoring] STATUS["status: pending → running →
completed | failed | cancelled"] DLQ[("Dead Letter Queue
maxRetries exhausted")] WAIT["dominus runs wait <id>
poll job_queue for status"] end subgraph Shutdown[Graceful Shutdown] STOP["Worker.stop()
clear poll timer"] DRAIN["await active jobs
(max gracefulShutdownTimeoutMs)"] FORCE["abort() remaining
Mark jobs as 'cancelled'"] end CLI --> E API --> E SCHED --> E E --> JQ POLL --> CLAIM CLAIM --> DISPATCH DISPATCH --> PRH DISPATCH --> PRH2 DISPATCH --> BTH DISPATCH --> BKH DISPATCH --> PRN DISPATCH --> WTH DISPATCH --> RNW DISPATCH --> WTT PRH -.-> ABORT PRH2 -.-> ABORT BTH -.-> ABORT JQ -.-> POLL PRH --> STATUS PRH2 --> STATUS BTH --> STATUS BKH --> STATUS PRN --> STATUS WTH --> STATUS RNW --> STATUS WTT --> STATUS STATUS --> DLQ STATUS --> WAIT STOP --> DRAIN DRAIN --> FORCE FORCE --> STATUS style Caller fill:#1a1a2e,stroke:#16213e,color:#eee style Enqueue fill:#16213e,stroke:#0f3460,color:#eee style Worker fill:#0f3460,stroke:#533483,color:#eee style Handlers fill:#16213e,stroke:#533483,color:#eee style Results fill:#2d1b69,stroke:#533483,color:#eee style Shutdown fill:#1a1a2e,stroke:#16213e,color:#ddd ``` ## Configuration | Parameter | Default | Description | |-----------|---------|-------------| | `concurrency` | 2 | Max parallel jobs | | `pollIntervalMs` | 5000 | Poll interval (adaptive backoff on SQLITE_BUSY) | | `maxRunningAgeMs` | 300000 (5 min) | Stale job reclamation threshold | | `gracefulShutdownTimeoutMs` | 30000 (30 s) | Max wait for active jobs on shutdown | ## Job States ``` pending → running → completed → failed (→ pending if retries remain) → cancelled ``` Jobs that exhaust `maxRetries` move to the **dead letter queue** for manual inspection via the DLQ repository.