Multi-table alignment
Why alignment is needed
In cascaded real-time analytics, your downstream target table is a Block table written at block granularity: all compute results for a height H are written atomically to the target table’s height H as one batch. So when you have several upstream tables, you must wait until every upstream table has finished processing H before you can compute H once and write it as a single batch — otherwise the H batch would be missing data and become a dirty batch. Even more importantly, compute functions often useget_state to read state snapshots from dependency tables. Which height version you can read depends on the dependency table’s consensus height: only when it is ≥ H can get_state return the state at height H; otherwise it fails because the consensus height is below H and you cannot get H’s state at all. So you must wait for the dependency table’s watermark to reach H or beyond before running the function.
Subscribing to several tables’ event streams directly gives you completely out-of-order height notifications, because each table computes at a different pace (for example trace has reached 18000010 while token is still at 18000005).
The SDK’s Aligned state machine separates these two kinds of waiting and performs the alignment automatically at the database change-stream layer.
Trigger tables and Depends tables
Aligned distinguishes two kinds of tables, each with its own alignment rule (similar to a multi-stream join, but the join conditions differ):
-
Trigger tables (
loose_align): the upstream data sources. The rule is “every Trigger table is ready at height H” — because the downstream writes H as one batch, and a single missing upstream table means the H batch is incomplete. -
Depends tables (
strict_align): the state tables your compute reads withget_state. The rule is “the Depends table’s consensus height is ≥ H” — becauseget_statecan only read snapshots at or below the consensus height, so a watermark below H cannot serve H’s state.
Complete example
How the consensus height is computed
Aligned computes the consensus height by querying each Depends table’s _height derived table:
- Every Block table has a
<table>._heighttable whose rows each record a processed height range (block_height_start/block_height_end); the write path already merges them into contiguous ranges. - The consensus height is the
block_height_endof the row with the smallest start height — the right boundary of the first contiguous range. - If the table has not been processed at all, the consensus height is
start_height - 1: for a table whose start height is not 0, heights below that start will never have data.
start_height (including empty blocks), its consensus height keeps advancing and its downstream consumers can be released accordingly.
Best practices
- Depends tables must submit a task for every block, including empty ones: an empty block still needs an empty task to advance the
_heightwatermark. Otherwise coverage develops a gap, the consensus height stalls before the gap, and the whole downstream pipeline stops silently. - A single-table subscription needs no
Aligned: just consumefor ... in Subscribe(...).listen()directly. - The same table can be both Trigger and Depends:
Alignedrecords its readiness first and then refreshes its watermark, so the diff range can see the height it just marked. Subscribehandles reconnection and cursor resumption internally; consume withfor ... in aligned.listen():and skip your own retry logic.