morca · API

orca.fill

Fills (#155): a pipeline filling its whole partition range by itself, within limits, set in orca.toml (orca.config):

[pipelines.gharchive] fills = "all" # "since_registration", the default fill_order = "oldest" # or "newest" fill_max_runs = 2 # fill runs at once, beside its other runs fill_budget_per_day = 5.0 # estimated US dollars a UTC day fill_window = "22..6" # UTC hours fill runs may start in fill_lag = "2h" # how long after its end a partition fills

With fills = "all" the scheduler queues, on each tick, runs (trigger fill) for the partitions of the pipeline's range that nothing has filled: from its first partition through the last one that has ended at least fill_lag ago, in fill_order, as long as every limit allows (fill_status).

fill_lag (0, the default, for none) is for sources that publish a partition's data some time after it ends: GH Archive puts each hour's file up minutes after the hour, so at 00:00 UTC the day that just ended isn't all there yet. A Daily day D fills from D+1 00:00 plus the lag (on the schedule's clock), a Monthly month from the 1st of the next plus the lag. Until then the partition is outside the range, for the fill and for the coverage alike (range_keys), so it isn't counted as missing; the pipeline page's Fill line says when it fills (FillStatus.lag_key). The default, since_registration, is what orca did before: a schedule fills from the pipeline's registration on, and orca backfill the rest by hand.

What a fill takes. A partition is left alone when it's done in a run of any code of the pipeline (every mapped step succeeded for it; with no mapped step, a run over it succeeded), when a live run has it, or when a fill run of it was ever queued, so a fill never runs a partition twice: one whose fill run failed past its retries, or was cancelled, is left to a person (orca backfill, or resume in the UI). Running done partitions again stays orca backfill --rerun. All of that is within the pipeline's current data version, its steps' version= (#297): raising one makes every partition fillable again, and the fill regenerates them within its limits.

Limits, each a reason the pipeline page gives for a fill being idle: the pipeline paused (by a person or by the watchdog, orca.watchdog), the hour outside fill_window, the day's budget, the pipeline's other queued runs (a scheduled or backfilled run starts first, so fills never starve them), and fill_max_runs. Fill runs don't count against max_runs.

On a worker pool (placement = "worker:<pool>") the scheduler queues the fill runs the same way, in the same order and within the same limits, and leaves them queued, held for a worker of the pool to claim (orca.workers). fill_max_runs caps the pipeline's live fill runs across the whole pool, queued ones included, so the fill never has more than that many going whatever the number of workers; each worker's --max-runs and its grants cap what one machine takes. A worker pool's runs cost nothing more than its machines, which run anyway.

The cost estimate is conservative, and fails closed. A fill run's cost is its instances' time (orca.instances) at orca.fleet's on-demand price table, times COST_MARGIN for what the table leaves out (EBS, transfer, requests). The day's spend is that over the fill runs' instances since 00:00 UTC, up to now. Before starting a run, orca also counts what the fill runs going and the new one may still cost: the pipeline's hourly price (head plus its most workers) for the longest fill run of its last _HISTORY (an hour if it has none, at least a minute, plus the warm pool's idle time on EC2), less what each has run already. A run starts only if the spend plus that stays within fill_budget_per_day. When a price isn't known (an instance type not in the table, or Kubernetes, whose cluster orca can't price), no fill run starts on a budget, and the reason says so. A pipeline whose runs cost money (placed on EC2 or Kubernetes) starts no fill run without a budget. Runs on the control host or local processes cost nothing more than the machine already running.

Keyed pipelines and sensors don't fill: orca register refuses fills = "all" for them (fill_refusal).

Structs

Fill

struct Fill

A pipeline's fill settings, as registered.

Implements Copyable, Movable

Fields
all: Bool: fills = "all".
newest_first: Bool
max_runs: Int
budget: Float64: US dollars a UTC day; -1 for none.
window_from: Int
window_to: Int
lag: Int: fill_lag in seconds: how long after its end a partition fills.

of

def of(fills: String, order: String, max_runs: Int, budget: Float64, window: String, lag: String = "") -> Self

From the registration's columns: empty or negative for what wasn't set.

in_window

def in_window(self, hour: Int) -> Bool

window_text

def window_text(self) -> String

FillPipeline

struct FillPipeline

What fill_status needs of a registered pipeline.

Implements Copyable, Movable

Fields
name: String
fill: Fill
kind: String
start: String
tz: String
paused: Bool
placed: String: Where its runs would start: control, ec2, kubernetes, local or worker:<pool>.
head_type: String
worker_type: String
workers: Int
pool_seconds: Int: On EC2, how long an instance idles in the warm pool after a run.
keys_per_run: Int: How many partitions a fill run takes (#240): 1 by default.

FillStatus

struct FillStatus

Where a pipeline's fill is, and how many fill runs may start now.

Implements Copyable, Movable

Fields
on: Bool
total: Int
done: Int
live: Int: Partitions of the range in a live run (of any trigger).
failed: Int: Partitions whose fill run was cancelled or failed past its retries, not done since: left to a person.
retrying: Int: Partitions whose fill run failed and waits for a retry.
left: Int: Partitions no run has taken yet.
running: Int: Fill runs running now.
queued: Int: Fill runs queued and not yet started: on a worker pool, those waiting for a worker to claim them. They count against fill_max_runs as the running ones do.
rate: Float64: Partitions an hour the fill runs did lately; 0 for none.
eta_seconds: Int: left at rate; -1 when there's no rate.
spent: Float64: Today's (UTC) estimated spend of the fill runs; -1 if unknown.
budget: Float64
hourly: Float64: A run's US dollars an hour; -1 if unknown.
expected_seconds: Int: How long the next fill run is expected to take.
next_cost: Float64: What the runs going and one more may still cost.
idle: String: Why no more fill runs start, or empty.
slots: Int: Fill runs that may start now, before the pipeline's own limits.
next_keys: List[String]: The partitions of each of the next fill runs, comma-separated, in fill_order, up to keys_per_run to a run; at most slots runs.
lag_key: String: The partition that has ended but waits for fill_lag, or empty.
lag_until: Int64: When lag_key becomes fillable, UTC seconds; 0 for none.
lag: Int: fill_lag, seconds.

__init__

def __init__(out self)

Functions

parse_fills

def parse_fills(v: String) -> String

parse_fill_order

def parse_fill_order(v: String) -> String

parse_fill_max_runs

def parse_fill_max_runs(v: String) -> Int

parse_fill_budget

def parse_fill_budget(v: String) -> Float64

US dollars a day, from 0 to 100000.

parse_fill_window

def parse_fill_window(v: String) -> Tuple[Int, Int]

A..B: fill runs start from hour A up to (not at) hour B, UTC; A after B wraps past midnight (22..6); 0..24 is all day.

parse_fill_lag

def parse_fill_lag(v: String) -> Int

A duration in seconds: 0, or a whole number of seconds, minutes, hours or days (90s, 90m, 2h, 1d), up to MAX_LAG_SECONDS.

lag_text

def lag_text(seconds: Int) -> String

seconds as fill_lag keeps it: 0, 2h, 90m or 45s.

money

def money(x: Float64) -> String

x dollars as $1.23.

fill_refusal

def fill_refusal(kind: String, start: String, sensor: Bool, pipeline: String) -> String

Why fills = "all" can't be registered for a pipeline, or empty.

last_ended

def last_ended(kind: String, tz: String, now: Int64, lag: Int = Int(0)) -> String

The latest partition that had ended lag seconds before now on the schedule's wall clock (UTC without one): the end of a fill's range. The partition still going is the schedule's; one that ended less than lag (fill_lag) ago waits.

lag_wait

def lag_wait(kind: String, tz: String, now: Int64, lag: Int) -> Tuple[String, Int64]

The partition that has ended by now but less than lag seconds ago, the oldest if several, and when it becomes fillable (its end plus lag, UTC seconds); ("", 0) if none waits.

range_keys

def range_keys(kind: String, start: String, tz: String, now: Int64, lag: Int = Int(0)) -> List[String]

Every partition from start through last_ended, oldest first.

run_hourly

def run_hourly(placed: String, head_type: String, worker_type: String, workers: Int) -> Float64

US dollars an hour of one of the pipeline's runs where it's placed: 0 on the control host, as a local process or on a worker pool's machines (which run anyway), the head and its most workers at on-demand prices on EC2 (a type left empty is the runner's, ORCA_EC2_HEAD_TYPE / ORCA_EC2_WORKER_TYPE), and -1 for a price that isn't known (a type not in orca.fleet's table, or Kubernetes).

unknown_type

def unknown_type(placed: String, head_type: String, worker_type: String, workers: Int) -> String

The instance type run_hourly couldn't price, or empty.

fill_status

def fill_status(mut tx: Transaction, p: FillPipeline, now: Int64, run_retries: Int) -> FillStatus

p's fill now: its progress, today's spend, whether fill runs may start and which partitions they'd take. Read in tx, which the scheduler holds with the pipeline's row locked.

lag_note

def lag_note(s: FillStatus) -> String

2026-11-08 fills from 2026-11-09 02:00 UTC (fill_lag 2h), or empty when no partition waits for fill_lag.

eta_text

def eta_text(seconds: Int) -> String

status_json

def status_json(s: FillStatus) -> String

The fill as /api/pipelines/<name> gives it.

Aliases

comptime ALL = "all"
comptime SINCE_REGISTRATION = "since_registration"

The default of fills: a schedule fills from registration on.

comptime FILL_TRIGGER = "fill"

orca.runs.trigger of a fill's runs.

comptime COST_MARGIN = 1.2

What the estimate adds to the price table's prices: EBS volumes, data transfer and requests aren't in it.

comptime NO_HISTORY_SECONDS = 3600

How long a fill run is expected to take before one has ended.

comptime MIN_SECONDS = 60

EC2 bills an instance for a minute at least.

comptime MAX_LAG_SECONDS = 2678400

The longest fill_lag: a month and a day.