Skip to content

Two tables that must agree

Multi-statement transactions on catalog-managed Unity Catalog tables: BEGIN ATOMIC across two tables, Delta time travel to undo a half-finished write, and what a rollback leaves in the table history.

Nobody has passed this one yet.

What you will learn

  • Why two writes that have to land together are not safe just because each one is atomic on its own.
  • How to put a table back to a known good version with time travel.
  • How to make both writes commit as one unit with BEGIN ATOMIC on catalog-managed tables.
  • What a failed transaction leaves behind, and how to prove it from the table history.

The problem

A ledger and a balance, written by two statements. The job dies between them, so the ledger has the new rows and the balance does not. Both tables look fine on their own, nothing fires, and the reports are wrong until somebody reconciles them by hand.

two statements, one batch ledger +300 postings balance never ran the two disagree the job dies between them, and both tables look fine on their own BEGIN ATOMIC ... END; ledger balance both, or neither

Set up

The starter builds both tables from spark.range in a schema of your own: 4,096 postings over 64 accounts, a balance derived from them, and a check that they agree. An account agrees when its balance equals the sum of its postings, which is the only question this lab asks.

Paste your salt into the first cell. One of the five answers is yours alone, so a notebook borrowed from somebody else fails that check and passes the rest.

The work

  1. Reproduce it. The starter posts a late batch the way the job does today and stops between the two statements. Count the accounts that now disagree.
  2. Get back to a state you can trust. Every write left a version behind and DESCRIBE HISTORY lists them, so you do not have to work out which 300 rows were yours.
  3. Replay it atomically. The same two writes, as one unit.
  4. Prove it holds. Fail a transaction halfway on purpose. The error is not the answer. What the ledger holds afterwards is, and so is what the history recorded while the block ran.

Steps 2 to 4 are yours to write.

The questions

key what to print
drift_accounts accounts whose balance disagrees with their ledger after the failed batch
abort_versions versions the ledger gained while the failing transaction ran
abort_rows rows in the ledger once that transaction has failed
mismatch_after accounts still disagreeing after the atomic replay
probe the balance of the account your salt picks out

Notes

A transaction needs every table it touches to be catalog-managed, which is a table property:

create table ... using delta tblproperties ('delta.feature.catalogManaged' = 'supported')

BEGIN ATOMIC ... END; commits whole or rolls back whole, and is the only form serverless compute runs. BEGIN TRANSACTION; ... COMMIT; needs a SQL warehouse. The docs tutorial covers both.

Inside a BEGIN ATOMIC block the range() table function is not available. Generate rows there with explode(sequence(0, 299)) as t(id), and keep range() for the cells outside the block.

We measure how the site is used. PostHog and Google Analytics, set to measurement only with advertising features off. We do not sell your data or share it for advertising, and you can turn this off. What this stores.