ObeliskDB docs Home Whitepaper Console

Change feeds & jobs

Change feeds and jobs are the engine behind standing questions: a change feed detects change with zero stored data, a job acts on it on a schedule or a gate — so an agent's question keeps answering itself as new data lands.

The Snowflake-dialect keywords are accepted as aliases, so CREATE STREAM
still creates a change feed and CREATE TASK still creates a job. The
Obelisk names below are the primary spelling.

Change feeds — CDC as version diffs

A change feed is a tiny metadata object: an offset into the source table's version timeline. No change data is ever stored — querying the feed computes the delta between the offset version and the current version from the immutable file sets:

merely rewritten by copy-on-write DML, not changed

CREATE CHANGE FEED f ON TABLE t;        -- offset = t's current version
SELECT * FROM f;                        -- source columns + metadata columns
SELECT SYSTEM$CHANGE_FEED_HAS_DATA('F');     -- cheap: compares two version numbers

Metadata columns: METADATA$ACTION (INSERT/DELETE), METADATA$ISUPDATE (always FALSE here — see divergences), METADATA$ROW_ID (content hash).

Consumption: the offset advances only when a DML statement reading the feed commits — INSERT INTO tgt SELECT … FROM f or CREATE TABLE x AS SELECT … FROM f. Reading alone never advances it, so a consumer that fails leaves the feed intact.

Append-only feeds (APPEND_ONLY = TRUE) see every inserted row — even rows deleted afterwards — and ignore deletes entirely. For insert-only tables (the usual EL landing pattern) they are exact and cheapest.

One divergence worth knowing: without per-row lineage, an UPDATE appears in a change feed as a DELETE+INSERT pair rather than a paired update row.

Jobs — scheduled statements and DAGs

CREATE JOB load_raw
  SCHEDULE = '5 MINUTES'
  WHEN SYSTEM$CHANGE_FEED_HAS_DATA('F')
  AS INSERT INTO staged SELECT * FROM f;

CREATE JOB refresh_marts AFTER load_raw
  AS EXECUTE PIPE nightly_sync;          -- jobs can run pipes too

ALTER JOB load_raw RESUME;               -- jobs are created SUSPENDED
EXECUTE JOB load_raw;                    -- manual run of the whole graph

closed gate records a SKIPPED run and never spins up an engine.

The scheduler

The scheduler runs inside obelisk ui (a tick every 5 seconds). Without the UI running, EXECUTE JOB and session.tasks.tick() still work — cron can call obelisk ingest run / a Python one-liner if you want headless scheduling.

Standing questions

Chain a pipe, a change feed, and a WHEN-gated job and a question answers itself: new rows land, the feed reports the delta as metadata, the gate opens, the job refreshes the downstream transform. The gate stays metadata-cheap when nothing changed, so an always-on standing question costs almost nothing at rest. Persist the answers it produces under Insights & memory, and keep the definitions it computes consistent through the Metrics & semantic layer.