Expand description
THE DATAFLOW RUNTIME (Phase 2): a pipeline as tasks on OS threads connected by bounded in-memory edges that carry Arrow batches and the two control signals — watermarks and checkpoint barriers — in band (design/2026-09-15-phase2-dataflow-scoping.md).
- A task owns one operator and runs a loop over its single inbox, into which every upstream edge sends tagged messages; FIFO per edge is what per-key order rests on.
- An edge is
sync_channel-bounded: a full inbox backpressures the sender. - Shuffle hashes the routing columns of a batch (
create_hashes) into vnodes and sends one sub-batch (take) per downstream task by its vnode range; forward sends whole batches to one task; broadcast sends the same batch to every downstream task. - Watermarks: each source emits
Watermark { source, ts }(None= idle); a task keeps the latest per (edge, source), fires its operator on the minimum over the active ones, and emits its own watermark downstream under its own id, so the whole graph shares one time. - Barriers and epochs: the coordinator issues epochs — periodically from the runtime’s own
ticker, and one last time on
stop— as aControl::Barrierto every source; a source records its position, reports aSnapshot, and forwardsBarrier { epoch }behind the data it already sent. A task with several input edges aligns — batches arriving on an edge that already delivered the barrier are held until every edge has — then snapshots its operator, forwards the barrier, and hands the snapshot to the coordinator. Sinks make their output durable andAck. When every task has reported an epoch (or finished), the epoch is complete: the coordinator sendsControl::Committo the sources, which commit the positions they recorded at that barrier. Offsets therefore never run ahead of durable output — the at-least-once gate, now across task boundaries. - Eos flows in order behind the last data and the final barrier; a task finishes when every input reached it.
The runtime is Kafka-free: sources and sinks are traits, tested here with in-memory ones.
Structs§
- Chained
- A sink CHAINED into a source’s task: the source’s batches go straight to the sink on the same
thread — no edge, no hop, no second thread to wake (the scoping note’s chaining, for the
stage that needs no reshuffle). At a barrier the sink makes its output durable FIRST, then the
source records its position; a sink that cannot (a failed flush) leaves the epoch without a
position, so the commit that follows commits nothing newer. The task reports one
Snapshotper epoch and noAck: the ack is implied by the order. - Completed
- An epoch every task reported (or had finished before): its snapshots, by task.
- Epoch
Staging - Commit-gated output staging for an exactly-once sink that has no external transaction of its
own (the Arrow file sink, the Flight sink) — the software analogue of the Kafka sink’s producer
transaction.
on_databuffers into the open epoch;on_barrierseals it (pre-commit);commitreleases every sealed epoch up to the committed one for the sink to make visible. Nothing is visible before its epoch commits, so a restart from the last committed checkpoint re-produces the same rows (deterministic keys) and each committed row appears exactly once. Uncommitted staged rows are dropped on stop — a replay re-emits them. (At-least-once sinks skip this and emit inon_data, unchanged.) - Epoch
Tracker - The coordinator’s book: which tasks reported which epoch, which tasks finished. Epochs
complete in order. An epoch completes when every expected task has reported it (or finished
before it) — for a single-process run that is every task
0..n; for one worker of a multi- process run it is only the tasks placed on this worker (the leader aggregates across workers). - Graph
- The graph under construction: add tasks, connect routes, then
start. - OpSnapshot
- What a task checkpoints at a barrier: a small
headwritten every epoch, and zero or more immutablefilesuploaded once. A source, or an operator whose whole state is the head, has no files (OpSnapshot::whole). - Out
- An operator’s outputs for one input event: batches to route, and its own watermark if it advanced. The task routes them.
- Probe
- One task’s probe: its state, the target of a send, and when it entered the state.
- Running
- A started dataflow: issue epochs, commit them, stop it, join it.
- State
File - An immutable local state file an operator produced at a barrier: content-stable, so a backend
that stores files (the object store) uploads it once and later epochs that still reference it
re-upload nothing.
min_time/max_timeare its event-time range, for the manifest and rescale. - Worker
Exchange - This worker’s exchange endpoints for a multi-process run: one
LinkSenderper peer worker it sends to, and oneLinkReceiverper peer it receives from (established by the membership handshake beforestart).Graph::startruns with none of this — a single-worker run where every edge is a localSyncSender; the multi-process path isGraph::start_worker, which derives the actual edge wiring from the placement.
Enums§
- Control
- What the coordinator tells a source.
- Event
- What the coordinator hears from tasks.
- Msg
- What flows on an edge.
- Poll
- Route
- Where a task’s output goes.
- Task
State - What a task is doing right now — the runtime’s own answer to “where is it stuck”: every task
keeps one, the coordinator reads them (
Running::task_states).
Traits§
- Operator
- What a task runs. Operators are single-threaded and own their state; the runtime never calls two methods concurrently.
- Sink
- A sink consumes batches and acks barriers.
- Source
- A source drives itself: the runtime calls
polluntil it returnsPoll::Done(or the coordinator stops it).
Functions§
- position_
record - The position record of a single-partition source (
partitionis its task index): the one shape the checkpoint reads a source’s positions from —topickeys the restore’s lookup,inmaps a partition to the next offset. Every in-process source writes its barrier record with this. - process_
cpu_ by_ thread - The CPU time of every thread in this process so far, summed by thread name (milliseconds):
the engine’s tasks by their
fv-task-Nnames, librdkafka’srdk:*threads, the rest. Empty where/procis absent. This is where a build’s cores go, at a glance. - shuffle
- Split a batch by vnode:
hash(columns) % vnodes→ the target whose range holds it. Returns one optional sub-batch per target, in target order. Missing routing columns hash as null. - thread_
cpu_ ms - The CPU time this thread has used so far (user + system), from
/proc/thread-self/stat;Nonewhere that file does not exist. Linux reports it in USER_HZ ticks, which is 100 on every architecture regardless of the kernel’s HZ. - vnode_
ranges - Contiguous vnode ranges for
taskstasks overvnodesvnodes.
Type Aliases§
- Acker
- Acks an epoch’s barrier from off the sink’s thread: a sink whose durability lands elsewhere (a
file committer syncing parts, a table commit) withholds the ack in
on_barrierand calls this once the epoch is durable — under its own task id, which the runtime binds in. - Deferred
Operator - An operator built once its task’s input edges are known:
(task id, in-edges with the upstream task each comes from)— a two-input operator learns which edges are which side.Errwhen it cannot be built (a checkpoint it refuses to restore): the whole start fails, before any task runs, with that message. - Deferred
Sink - A sink built at start, on the worker that owns its task, knowing its task id — for a sink that touches a shared resource (a durable file sink’s directory) and must be opened by its owner only, and one that acks its barriers from another thread under its own id. A builder that fails fails the start before any task runs, exactly as a deferred operator’s does.
- EdgeId
- An edge’s id: unique within one running graph, and the same on every worker.
- Hasher
- Hashes the routing columns of a batch into
out(oneu64per row) — the shuffle’s key hash, supplied by the planner (the engine’s DataFusion hash, fixed-seed: a key’s vnode is the same on every run, every worker and every restart). The runtime never chooses a hash function. - TaskId
- A task’s id: unique within one running graph.