Jobs¶
A job is a named, schedulable workload: it declares what to materialize and, for a cron job, when. The core carries the declaration and compiles it to the same DAG every other entry point runs. Acting on the schedule is the job of the platform scheduler, outside this site.
Job¶
import interloper as il
job = il.Job(
targets=[Shop(account_id="act_1"), Finance()],
destinations=[il.CSVDestination(base_path="./data")],
tags=["daily"],
)
il.DAG(job).materialize(partition)
| Field | Meaning |
|---|---|
targets |
Sources and assets to materialize. Their operations are flattened into the DAG. |
destinations |
Defaults for any target that declares none. |
enabled |
A disabled job is kept but not scheduled. |
tags |
Free-form labels. |
A job's destinations are the default for every target that resolves none: a source with its own
destinations, or an asset whose source has some, keeps them. The default applies where the job
runs (job.operations(), il.DAG(job)), on a copy of the target, so the target itself is never
changed and two jobs sharing it each run it with their own. A job subclass that declares a
further relation (a connection, say) is a default for its targets the same way.
JobState (next_run_at, last_run_at) is the job's machine-owned state, written by the
scheduler. Editing a job's config clears next_run_at, so the next tick re-derives the schedule
from the new spec rather than firing once more at the slot the old one produced.
CronJob¶
CronJob adds the trigger:
job = il.CronJob(
cron="0 6 * * *",
timezone="Europe/Berlin",
lookback=3,
offset=1,
targets=[Shop(account_id="act_1")],
)
| Field | Default | Meaning |
|---|---|---|
cron |
required | When the job runs, on the wall clock of timezone. |
timezone |
"UTC" |
IANA zone the schedule is evaluated in. Unknown names are rejected. |
lookback |
1 |
How many partitions each run covers. |
offset |
1 |
How many partitions back from the current one the window ends. |
concurrency |
1 |
How many partitions of one firing run at once. |
For daily targets the defaults mean "yesterday, in the job's timezone". offset=3, lookback=3
covers the three days ending three days ago, for a vendor whose numbers settle late. The window
itself is computed with TimePartitionWindow.lookback; hourly targets always use UTC windows
because hour ids are UTC labels. Whether a job is partitioned is derived from its targets, never
stored. Each firing is a backfill over the window, dispatched newest partition first and gated by
concurrency: at 1 the partitions run one at a time.
On the platform, a firing is quota-checked like a manual backfill: its window counts against the
organisation's max_backfill_partitions, and it needs the monthly run quota. A firing the quotas
reject is skipped, logged as a warning event on the job, and the job moves on to its next
scheduled time.
Jobs as specs¶
A job is the natural unit for a declarative run. Its spec lists targets with their configuration
and the workload-level defaults. A connection a target needs is not a job field, so it is nested
under that target's own relation name, in the target's own init:
path: interloper.job.base.Job
init:
destinations:
- key: bigquery_destination
init:
project: dwh
location: EU
connection:
key: google_cloud_connection
init:
service_account_key: ${GCP_KEY}
targets:
- key: facebook_ads
init:
account_id: act_1
connection:
key: facebook_ads_connection
init:
access_token: ${FB_TOKEN}
app_id: "1"
app_secret: ${FB_SECRET}
assets: {campaigns: {}, ads_stats: {}}
interloper run -f job.yaml --date 2026-01-15 reconstructs it and runs its DAG. See
Specs and serialization and CLI. A scheduled job is the same document
with key: cron_job and the trigger fields.
Relations¶
A job's relation vocabulary is targets (sources and assets) and destinations. A target is an
orchestration pointer, not an input: deleting a target shrinks the job rather than blocking the
deletion. Relation semantics are described in the
component model.