The scheduler: fires schedules, senses datasets, queues backfills and rebuilds, and starts runs.
orca register ./build/taxi runs `taxi describe`, records the binary
orca backfill taxi 2011-01..2024-12 a run per partition that isn't done
orca backfill labels @all keyed: runs of keys_per_run undone keys
orca rebuild taxi.trips 2024-01 the pipelines downstream, in order
orca tick one pass, then exits
orca scheduler a pass every second
The scheduler links no pipeline. It knows one by the describe it printed,
kept in orca.pipelines with the binary's path, and reads the schedule,
partitions and limits from that JSON in SQL. Everything it decides is in
Postgres, so it can be stopped and restarted, or run twice, at any time:
- A fire is a row.
orca.fires is keyed by (pipeline, fire time), and a
fire's row and its queued run are inserted in one transaction, so a fire
is never made twice. Fires are counted from the last one recorded (or from
when the pipeline was registered), so the fires missed while the scheduler
was down are made once each, or, with catchup=False, only the latest.
A fire whose partition is before the pipeline's first is recorded without
a run.
- Starting a run is
resume. A queued run has nothing done, or what a
resume or rerun from orca ui left it, which is what <binary> resume <run> takes (with --allow-new-code when the resume said so). The scheduler marks the run running,
with no heartbeat and itself as owner, and execs the binary, which claims
it. It doesn't wait: the run records its own progress.
max_runs. Under a lock on the pipeline's row, the runs that are live
(a fresh heartbeat, or started by the scheduler less than the stale limit
ago) are counted, and only that many fewer than max_runs are started.
- Backfills queue what isn't done. A key is done when every mapped
step has succeeded for it in a run of the pipeline's current code
(
DONE_SQL). A backfill drops the keys that are done or in a queued or
running run, sorts the rest and queues a run per keys_per_run of them.
- Where runs run.
ORCA_RUNNER is local (a process here, the
default), kubernetes (a dista cluster of pods per run, orca.kube) or
ec2 (a head instance and worker instances per run, orca.ec2).
- Sensors and rebuilds (
orca.sensors). A sensor pipeline's pass
looks for snapshots of the datasets it reads, in orca.commits on every
tick and from <binary> sense every ORCA_SENSE_SECONDS, and queues a
run, trigger sensor, for those it hasn't seen. A keyed sensor's keys
dataset is sensed too: a new snapshot of it queues the keys that aren't
done, in runs of keys_per_run. Each rebuild queues its
next level once the one before has succeeded.
- Retries. A scheduled, backfilled or sensed run that failed is started again
after
ORCA_RETRY_SECONDS (default 60) doubling with each start, plus up
to a tenth more as jitter, up to run_retries times. One whose process
died (its heartbeat went stale) is started again at once, within the same
budget. A manual run is never retried by the scheduler. A run asked to
cancel whose process died ends cancelled.
The scheduler's clock is orca.civil.now, which ORCA_NOW fixes; fire
times and retry times are on it. Heartbeats are on the database's clock.
A run's output goes to <ORCA_LOGS>/<pipeline>/<run>/driver.log when
ORCA_LOGS is set: written there by a local run, published there when the
process is reaped if ORCA_LOGS is in object storage, or kept from the
driver pod's log on Kubernetes (orca.kube).