<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic All 18 Lakeflow AUTO CDC configurations went green. Five failed my ship check in Community Articles</title>
    <link>https://community.databricks.com/t5/community-articles/all-18-lakeflow-auto-cdc-configurations-went-green-five-failed/m-p/167235#M1520</link>
    <description>&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Paper event slips trace 18 Lakeflow AUTO CDC configurations: 13 stay on the main sequence while five branch into production stop signs." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30626iEDB2F22254223A83/image-size/large?v=v2&amp;amp;px=999" role="button" title="exec-68d729e6-990b-4352-822c-6eb77f737c6b.png" alt="Paper event slips trace 18 Lakeflow AUTO CDC configurations: 13 stay on the main sequence while five branch into production stop signs." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Paper event slips trace 18 Lakeflow AUTO CDC configurations: 13 stay on the main sequence while five branch into production stop signs.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Change data capture (CDC) keeps a downstream table in step with row-level inserts, updates, and deletes instead of reloading the whole source. Lakeflow AUTO CDC handles the state-management work, including sequencing, deletes, and SCD history. It still needs a precise source contract: which clock wins, how ties break, what NULL means, and which changes deserve history.&lt;/P&gt;&lt;P&gt;I built this experiment to see what happens when source data is late, duplicated, contradictory, or noisy, and to separate a pipeline that finishes from a target I would trust. The suite pushes nine hostile CDC patterns across 13 isolated source tables and 18 AUTO CDC configurations: duplicates, late events, tied sequence values, conflicting clocks, sparse NULL updates, deletes, replays, sync-noise updates, and bitemporal corrections.&lt;/P&gt;&lt;P&gt;All 18 configurations completed. Five green configurations failed my ship check: three had complete ordering but violated the stated business rule, and two used incomplete ordering.&lt;/P&gt;&lt;P&gt;Start with scenario 4. Ordering the same rows by ingestion time kept ACTIVE; source event time produced SUSPENDED, the expected business state. Both configurations finished green.&lt;/P&gt;&lt;P&gt;I used pipeline status to confirm execution. I used target-state assertions to decide whether I would ship.&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;&lt;STRONG&gt;Independent experiment.&lt;/STRONG&gt; I ran this test for my own engineering work. It is not official Databricks guidance. I’m happy to discuss your results, feedback, and the failure modes you think I missed.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;&lt;STRONG&gt;&lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test" target="_blank" rel="noopener"&gt;Run the experiment&lt;/A&gt;&lt;/STRONG&gt; · &lt;STRONG&gt;&lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/normalized/summary_matrix.json" target="_blank" rel="noopener"&gt;Inspect the result matrix&lt;/A&gt;&lt;/STRONG&gt;&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Results in one minute&lt;/H2&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;10 handled:&lt;/STRONG&gt; keep the sequence rule and test it against your source.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;3 configuration-dependent:&lt;/STRONG&gt; set the option that matches the business rule.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;3 business-semantics stops:&lt;/STRONG&gt; change the chosen clock, NULL meaning, or history policy.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;2 ambiguous-order stops:&lt;/STRONG&gt; add a source-side tie-breaker.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Execution stayed green across all five stop signs. Source semantics or ordering failed my ship check.&lt;/P&gt;&lt;H2&gt;The five stop signs&lt;/H2&gt;&lt;P&gt;Configuration Measured result Fix before production&lt;/P&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;3A Sequence collision&lt;/TD&gt;&lt;TD&gt;Two business states shared one sequence value. The contract names no winner.&lt;/TD&gt;&lt;TD&gt;Reject ties or add a stable source-side tie-breaker.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;3B Tie-breaker ignored&lt;/TD&gt;&lt;TD&gt;The source supplied transaction_sequence, but the flow left it out of SEQUENCE BY.&lt;/TD&gt;&lt;TD&gt;Use a composite sequence.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;4A Ingestion-time order&lt;/TD&gt;&lt;TD&gt;The target kept ACTIVE; source time required SUSPENDED.&lt;/TD&gt;&lt;TD&gt;Use the clock that defines business recency.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;5A Default NULL handling&lt;/TD&gt;&lt;TD&gt;A sparse update replaced the existing email with NULL.&lt;/TD&gt;&lt;TD&gt;Define NULL semantics and use IGNORE NULL UPDATES when NULL means absent.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;8A Track every column&lt;/TD&gt;&lt;TD&gt;Fifty sync-timestamp updates created 51 SCD2 rows.&lt;/TD&gt;&lt;TD&gt;Exclude operational metadata from history tracking.&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;The other 13 configurations matched the experiment’s business rule under a complete order and the required option.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;How I tested it&lt;/H2&gt;&lt;P&gt;The generator creates small, isolated source tables with one failure mode per scenario. One pipeline wraps each source in a streaming view and runs all 18 AUTO CDC configurations.&lt;/P&gt;&lt;P&gt;For late events and replays, I ran two updates. The full refresh established a baseline. The incremental update appended the withheld rows. The verifier required the two expected histories to change, the other 16 targets to remain equal, and every target to match its row-count and observed-state predicate. The final classification combined those measurements with the declared ordering and business-rule labels.&lt;/P&gt;&lt;P&gt;You can inspect the code in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/src/pipeline/pipeline.py" target="_blank" rel="noopener"&gt;src/pipeline/pipeline.py&lt;/A&gt; and &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/src/generators/dispatch.py" target="_blank" rel="noopener"&gt;src/generators/dispatch.py&lt;/A&gt;. The &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/normalized/summary_matrix.json" target="_blank" rel="noopener"&gt;result matrix&lt;/A&gt; and &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/raw/target_state.json" target="_blank" rel="noopener"&gt;captured target rows&lt;/A&gt; carry the measured evidence. Official Databricks documentation used for the platform claims is indexed in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/docs/sources.md" target="_blank" rel="noopener"&gt;docs/sources.md&lt;/A&gt;.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Four source decisions that change the answer&lt;/H2&gt;&lt;H3&gt;1. Pick the clock that means “newer”&lt;/H3&gt;&lt;P&gt;The generator emits two events for the same key:&lt;/P&gt;&lt;PRE&gt;10:00 source time  ACTIVE
10:05 source time  SUSPENDED&lt;/PRE&gt;&lt;P&gt;Their ingestion timestamps reverse that order. Ordering by ingested_at keeps ACTIVE. Ordering by source_updated_at produces SUSPENDED, which matches the experiment’s business rule.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Two AUTO CDC targets show that ingestion time misses the business expectation while source time matches it." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30627i88EA6F2049895B73/image-size/large?v=v2&amp;amp;px=999" role="button" title="wrong_clock.png" alt="Two AUTO CDC targets show that ingestion time misses the business expectation while source time matches it." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Two AUTO CDC targets show that ingestion time misses the business expectation while source time matches it.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Two AUTO CDC targets use the same rows and schema. The sequence clock changes the answer.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;Arrival time helps you analyze transport; source time can define business recency. Pick the column from the business definition before you write the flow.&lt;/P&gt;&lt;H3&gt;2. Give ties a real winner&lt;/H3&gt;&lt;P&gt;The collision pattern gives two states the same source_sequence:&lt;/P&gt;&lt;PRE&gt;seq=10  status=ACTIVE
seq=10  status=SUSPENDED&lt;/PRE&gt;&lt;P&gt;I observed SUSPENDED, but the configured order cannot distinguish the rows, so I recorded the result as AMBIGUOUS_ORDER.&lt;/P&gt;&lt;P&gt;The source also provides transaction_sequence. A composite sequence such as STRUCT(source_updated_at, transaction_sequence) gives the flow a stable order. The composite configuration produced the expected SUSPENDED state, and the verifier classified it as HANDLED.&lt;/P&gt;&lt;H3&gt;3. Decide what NULL means&lt;/H3&gt;&lt;P&gt;The sparse-update pattern begins with email='x@example.com'. The next event updates the city and carries email=NULL.&lt;/P&gt;&lt;P&gt;The default configuration sets the target email to NULL. IGNORE NULL UPDATES keeps the existing email. Both behaviors can serve a valid source contract. Your producer must define whether NULL means “erase this value” or “this field was absent.”&lt;/P&gt;&lt;H3&gt;4. Keep sync noise out of business history&lt;/H3&gt;&lt;P&gt;The history-noise generator emits 50 updates. Among the retained target columns, only last_synced_at changes.&lt;/P&gt;&lt;P&gt;Tracking every included target column creates 51 SCD2 rows. Excluding last_synced_at creates one row because the business fields never change.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Tracking every included target column produces 51 SCD2 rows; excluding last_synced_at produces one." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30628i882EC60FE5C94C52/image-size/large?v=v2&amp;amp;px=999" role="button" title="scd2_history_noise.png" alt="Tracking every included target column produces 51 SCD2 rows; excluding last_synced_at produces one." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Tracking every included target column produces 51 SCD2 rows; excluding last_synced_at produces one.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Operational sync metadata accounts for all 50 extra versions.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;Choose the columns that represent business history before you deploy SCD2. Whether operational changes belong there is a domain and audit decision; this experiment’s business rule counted only business-field changes.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Patterns AUTO CDC handled&lt;/H2&gt;&lt;P&gt;Pattern Measured result&lt;/P&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;Identical duplicate and late replay&lt;/TD&gt;&lt;TD&gt;SCD1 kept one ACTIVE row. The replay left visible state unchanged.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Out-of-order update&lt;/TD&gt;&lt;TD&gt;SCD1 kept the newer state. SCD2 inserted the older event as a closed history row.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Delete followed by an older event&lt;/TD&gt;&lt;TD&gt;SCD1 kept the deletion. SCD2 inserted the late state before the delete boundary.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Full lifecycle replay&lt;/TD&gt;&lt;TD&gt;SCD1 and SCD2 matched their saved baselines after the replay.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Bitemporal correction&lt;/TD&gt;&lt;TD&gt;The target preserved business time and system time across five measured rows.&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;These are visible-state observations from appended replays. They do not establish transactional deduplication or test a checkpoint-only restart with no new source rows.&lt;/P&gt;&lt;H3&gt;Bitemporal kept both clocks&lt;/H3&gt;&lt;P&gt;Scenario 9 uses source_updated_at for business time and ingested_at for system time. The target stores __START_AT / __END_AT beside __SYSTEM_START_AT / __SYSTEM_END_AT.&lt;/P&gt;&lt;P&gt;Three events produced five rows because later ingestion times revised earlier valid-time intervals.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Five measured bitemporal rows show original and revised valid-time intervals across three system times." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30629i605D493792A77755/image-size/large?v=v2&amp;amp;px=999" role="button" title="bitemporal_timeline.png" alt="Five measured bitemporal rows show original and revised valid-time intervals across three system times." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Five measured bitemporal rows show original and revised valid-time intervals across three system times.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Five rows preserve the original and revised valid-time intervals.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;Databricks marks bitemporal storage as Beta. This run validates five tiny rows and does not test load. I would evaluate it when valid time differs from ingest time and consumers need as-of-system-time queries.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;The full result map&lt;/H2&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="All 18 measured configurations grouped by handled, configuration-dependent, business-semantics, and ambiguous-order outcomes." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30630i34D53099C293954C/image-size/large?v=v2&amp;amp;px=999" role="button" title="summary_matrix.png" alt="All 18 measured configurations grouped by handled, configuration-dependent, business-semantics, and ambiguous-order outcomes." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;All 18 measured configurations grouped by handled, configuration-dependent, business-semantics, and ambiguous-order outcomes.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;All 18 measured configurations grouped by production outcome.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;You can inspect the machine-readable matrix in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/normalized/summary_matrix.json" target="_blank" rel="noopener"&gt;results/normalized/summary_matrix.json&lt;/A&gt; and the captured rows in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/raw/target_state.json" target="_blank" rel="noopener"&gt;results/raw/target_state.json&lt;/A&gt;.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;My production checklist&lt;/H2&gt;&lt;OL&gt;&lt;LI&gt;&lt;STRONG&gt;Name the business clock.&lt;/STRONG&gt; Put the column that defines recency in SEQUENCE BY.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Search for ties per key.&lt;/STRONG&gt; Add a source-side tie-breaker when two states can share one sequence value.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Write down NULL semantics.&lt;/STRONG&gt; Use IGNORE NULL UPDATES only when NULL means absent.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Choose the state model.&lt;/STRONG&gt; SCD1 keeps current state; SCD2 keeps ordered history.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Filter history noise.&lt;/STRONG&gt; Exclude sync metadata unless it represents a business event.&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;Add a target-state predicate for each rule: update status checks execution, while the predicate checks the expected target state.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Reproduce the run&lt;/H2&gt;&lt;PRE&gt;git clone https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test.git
cd lakeflow-auto-cdc-torture-test
python -m pip install -e ".[dev]"
databricks auth login --host https://&amp;lt;workspace-url&amp;gt; --profile DEFAULT
make setup
make test
make results&lt;/PRE&gt;&lt;P&gt;You need Python 3.10 or newer, GNU Make, jq, the Databricks CLI, and access to a Databricks workspace. The &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/docs/reproduction.md" target="_blank" rel="noopener"&gt;reproduction guide&lt;/A&gt; covers the one-time setup and evidence capture.&lt;/P&gt;&lt;P&gt;Sixteen targets matched their baselines after update 2. The two SCD2 targets that received late history changed as expected. The verifier checked all 18 targets and produced 10 HANDLED, 3 CONFIGURATION_DEPENDENT, 3 BUSINESS_SEMANTICS, and 2 AMBIGUOUS_ORDER results.&lt;/P&gt;&lt;P&gt;The checked-in result set was captured on 2026-09-01 from clean commit 01d53b4; target_state.json records the pipeline and both update IDs.&lt;/P&gt;&lt;H2&gt;Scope&lt;/H2&gt;&lt;P&gt;This suite uses tiny controlled datasets. I did not test throughput, backpressure, or large joins. Each flow read one isolated source table; I did not test joins or interactions across streams. The delete-and-late-event case stayed inside the configured 48-hour tombstone-retention window. Bitemporal load behavior also sits outside this run.&lt;/P&gt;&lt;P&gt;The evidence comes from one workspace, one SQL warehouse, one customer key, serverless Advanced-edition compute on the CURRENT channel, and tiny deterministic inputs. Use separate tests for throughput, schema evolution, and multi-stream joins.&lt;/P&gt;&lt;H2&gt;Bring your failure mode&lt;/H2&gt;&lt;P&gt;Clone the repository and replace one generator with an event sequence from your source. Add the expected target state, run both phases, and compare the capture.&lt;/P&gt;&lt;P&gt;If the suite misses your case, &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/issues/new" target="_blank" rel="noopener"&gt;open an issue&lt;/A&gt; with the source rows, SEQUENCE BY expression, storage type, expected target, and observed target. I’m happy to turn a clear failure report into another reproducible scenario.&lt;/P&gt;</description>
    <pubDate>Wed, 02 Sep 2026 01:54:46 GMT</pubDate>
    <dc:creator>ivanvyd</dc:creator>
    <dc:date>2026-09-02T01:54:46Z</dc:date>
    <item>
      <title>All 18 Lakeflow AUTO CDC configurations went green. Five failed my ship check</title>
      <link>https://community.databricks.com/t5/community-articles/all-18-lakeflow-auto-cdc-configurations-went-green-five-failed/m-p/167235#M1520</link>
      <description>&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Paper event slips trace 18 Lakeflow AUTO CDC configurations: 13 stay on the main sequence while five branch into production stop signs." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30626iEDB2F22254223A83/image-size/large?v=v2&amp;amp;px=999" role="button" title="exec-68d729e6-990b-4352-822c-6eb77f737c6b.png" alt="Paper event slips trace 18 Lakeflow AUTO CDC configurations: 13 stay on the main sequence while five branch into production stop signs." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Paper event slips trace 18 Lakeflow AUTO CDC configurations: 13 stay on the main sequence while five branch into production stop signs.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Change data capture (CDC) keeps a downstream table in step with row-level inserts, updates, and deletes instead of reloading the whole source. Lakeflow AUTO CDC handles the state-management work, including sequencing, deletes, and SCD history. It still needs a precise source contract: which clock wins, how ties break, what NULL means, and which changes deserve history.&lt;/P&gt;&lt;P&gt;I built this experiment to see what happens when source data is late, duplicated, contradictory, or noisy, and to separate a pipeline that finishes from a target I would trust. The suite pushes nine hostile CDC patterns across 13 isolated source tables and 18 AUTO CDC configurations: duplicates, late events, tied sequence values, conflicting clocks, sparse NULL updates, deletes, replays, sync-noise updates, and bitemporal corrections.&lt;/P&gt;&lt;P&gt;All 18 configurations completed. Five green configurations failed my ship check: three had complete ordering but violated the stated business rule, and two used incomplete ordering.&lt;/P&gt;&lt;P&gt;Start with scenario 4. Ordering the same rows by ingestion time kept ACTIVE; source event time produced SUSPENDED, the expected business state. Both configurations finished green.&lt;/P&gt;&lt;P&gt;I used pipeline status to confirm execution. I used target-state assertions to decide whether I would ship.&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;&lt;STRONG&gt;Independent experiment.&lt;/STRONG&gt; I ran this test for my own engineering work. It is not official Databricks guidance. I’m happy to discuss your results, feedback, and the failure modes you think I missed.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;&lt;STRONG&gt;&lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test" target="_blank" rel="noopener"&gt;Run the experiment&lt;/A&gt;&lt;/STRONG&gt; · &lt;STRONG&gt;&lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/normalized/summary_matrix.json" target="_blank" rel="noopener"&gt;Inspect the result matrix&lt;/A&gt;&lt;/STRONG&gt;&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Results in one minute&lt;/H2&gt;&lt;UL&gt;&lt;LI&gt;&lt;STRONG&gt;10 handled:&lt;/STRONG&gt; keep the sequence rule and test it against your source.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;3 configuration-dependent:&lt;/STRONG&gt; set the option that matches the business rule.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;3 business-semantics stops:&lt;/STRONG&gt; change the chosen clock, NULL meaning, or history policy.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;2 ambiguous-order stops:&lt;/STRONG&gt; add a source-side tie-breaker.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Execution stayed green across all five stop signs. Source semantics or ordering failed my ship check.&lt;/P&gt;&lt;H2&gt;The five stop signs&lt;/H2&gt;&lt;P&gt;Configuration Measured result Fix before production&lt;/P&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;3A Sequence collision&lt;/TD&gt;&lt;TD&gt;Two business states shared one sequence value. The contract names no winner.&lt;/TD&gt;&lt;TD&gt;Reject ties or add a stable source-side tie-breaker.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;3B Tie-breaker ignored&lt;/TD&gt;&lt;TD&gt;The source supplied transaction_sequence, but the flow left it out of SEQUENCE BY.&lt;/TD&gt;&lt;TD&gt;Use a composite sequence.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;4A Ingestion-time order&lt;/TD&gt;&lt;TD&gt;The target kept ACTIVE; source time required SUSPENDED.&lt;/TD&gt;&lt;TD&gt;Use the clock that defines business recency.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;5A Default NULL handling&lt;/TD&gt;&lt;TD&gt;A sparse update replaced the existing email with NULL.&lt;/TD&gt;&lt;TD&gt;Define NULL semantics and use IGNORE NULL UPDATES when NULL means absent.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;8A Track every column&lt;/TD&gt;&lt;TD&gt;Fifty sync-timestamp updates created 51 SCD2 rows.&lt;/TD&gt;&lt;TD&gt;Exclude operational metadata from history tracking.&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;The other 13 configurations matched the experiment’s business rule under a complete order and the required option.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;How I tested it&lt;/H2&gt;&lt;P&gt;The generator creates small, isolated source tables with one failure mode per scenario. One pipeline wraps each source in a streaming view and runs all 18 AUTO CDC configurations.&lt;/P&gt;&lt;P&gt;For late events and replays, I ran two updates. The full refresh established a baseline. The incremental update appended the withheld rows. The verifier required the two expected histories to change, the other 16 targets to remain equal, and every target to match its row-count and observed-state predicate. The final classification combined those measurements with the declared ordering and business-rule labels.&lt;/P&gt;&lt;P&gt;You can inspect the code in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/src/pipeline/pipeline.py" target="_blank" rel="noopener"&gt;src/pipeline/pipeline.py&lt;/A&gt; and &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/src/generators/dispatch.py" target="_blank" rel="noopener"&gt;src/generators/dispatch.py&lt;/A&gt;. The &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/normalized/summary_matrix.json" target="_blank" rel="noopener"&gt;result matrix&lt;/A&gt; and &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/raw/target_state.json" target="_blank" rel="noopener"&gt;captured target rows&lt;/A&gt; carry the measured evidence. Official Databricks documentation used for the platform claims is indexed in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/docs/sources.md" target="_blank" rel="noopener"&gt;docs/sources.md&lt;/A&gt;.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Four source decisions that change the answer&lt;/H2&gt;&lt;H3&gt;1. Pick the clock that means “newer”&lt;/H3&gt;&lt;P&gt;The generator emits two events for the same key:&lt;/P&gt;&lt;PRE&gt;10:00 source time  ACTIVE
10:05 source time  SUSPENDED&lt;/PRE&gt;&lt;P&gt;Their ingestion timestamps reverse that order. Ordering by ingested_at keeps ACTIVE. Ordering by source_updated_at produces SUSPENDED, which matches the experiment’s business rule.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Two AUTO CDC targets show that ingestion time misses the business expectation while source time matches it." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30627i88EA6F2049895B73/image-size/large?v=v2&amp;amp;px=999" role="button" title="wrong_clock.png" alt="Two AUTO CDC targets show that ingestion time misses the business expectation while source time matches it." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Two AUTO CDC targets show that ingestion time misses the business expectation while source time matches it.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Two AUTO CDC targets use the same rows and schema. The sequence clock changes the answer.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;Arrival time helps you analyze transport; source time can define business recency. Pick the column from the business definition before you write the flow.&lt;/P&gt;&lt;H3&gt;2. Give ties a real winner&lt;/H3&gt;&lt;P&gt;The collision pattern gives two states the same source_sequence:&lt;/P&gt;&lt;PRE&gt;seq=10  status=ACTIVE
seq=10  status=SUSPENDED&lt;/PRE&gt;&lt;P&gt;I observed SUSPENDED, but the configured order cannot distinguish the rows, so I recorded the result as AMBIGUOUS_ORDER.&lt;/P&gt;&lt;P&gt;The source also provides transaction_sequence. A composite sequence such as STRUCT(source_updated_at, transaction_sequence) gives the flow a stable order. The composite configuration produced the expected SUSPENDED state, and the verifier classified it as HANDLED.&lt;/P&gt;&lt;H3&gt;3. Decide what NULL means&lt;/H3&gt;&lt;P&gt;The sparse-update pattern begins with email='x@example.com'. The next event updates the city and carries email=NULL.&lt;/P&gt;&lt;P&gt;The default configuration sets the target email to NULL. IGNORE NULL UPDATES keeps the existing email. Both behaviors can serve a valid source contract. Your producer must define whether NULL means “erase this value” or “this field was absent.”&lt;/P&gt;&lt;H3&gt;4. Keep sync noise out of business history&lt;/H3&gt;&lt;P&gt;The history-noise generator emits 50 updates. Among the retained target columns, only last_synced_at changes.&lt;/P&gt;&lt;P&gt;Tracking every included target column creates 51 SCD2 rows. Excluding last_synced_at creates one row because the business fields never change.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Tracking every included target column produces 51 SCD2 rows; excluding last_synced_at produces one." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30628i882EC60FE5C94C52/image-size/large?v=v2&amp;amp;px=999" role="button" title="scd2_history_noise.png" alt="Tracking every included target column produces 51 SCD2 rows; excluding last_synced_at produces one." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Tracking every included target column produces 51 SCD2 rows; excluding last_synced_at produces one.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Operational sync metadata accounts for all 50 extra versions.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;Choose the columns that represent business history before you deploy SCD2. Whether operational changes belong there is a domain and audit decision; this experiment’s business rule counted only business-field changes.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Patterns AUTO CDC handled&lt;/H2&gt;&lt;P&gt;Pattern Measured result&lt;/P&gt;&lt;TABLE&gt;&lt;TBODY&gt;&lt;TR&gt;&lt;TD&gt;Identical duplicate and late replay&lt;/TD&gt;&lt;TD&gt;SCD1 kept one ACTIVE row. The replay left visible state unchanged.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Out-of-order update&lt;/TD&gt;&lt;TD&gt;SCD1 kept the newer state. SCD2 inserted the older event as a closed history row.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Delete followed by an older event&lt;/TD&gt;&lt;TD&gt;SCD1 kept the deletion. SCD2 inserted the late state before the delete boundary.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Full lifecycle replay&lt;/TD&gt;&lt;TD&gt;SCD1 and SCD2 matched their saved baselines after the replay.&lt;/TD&gt;&lt;/TR&gt;&lt;TR&gt;&lt;TD&gt;Bitemporal correction&lt;/TD&gt;&lt;TD&gt;The target preserved business time and system time across five measured rows.&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;P&gt;These are visible-state observations from appended replays. They do not establish transactional deduplication or test a checkpoint-only restart with no new source rows.&lt;/P&gt;&lt;H3&gt;Bitemporal kept both clocks&lt;/H3&gt;&lt;P&gt;Scenario 9 uses source_updated_at for business time and ingested_at for system time. The target stores __START_AT / __END_AT beside __SYSTEM_START_AT / __SYSTEM_END_AT.&lt;/P&gt;&lt;P&gt;Three events produced five rows because later ingestion times revised earlier valid-time intervals.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Five measured bitemporal rows show original and revised valid-time intervals across three system times." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30629i605D493792A77755/image-size/large?v=v2&amp;amp;px=999" role="button" title="bitemporal_timeline.png" alt="Five measured bitemporal rows show original and revised valid-time intervals across three system times." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;Five measured bitemporal rows show original and revised valid-time intervals across three system times.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;Five rows preserve the original and revised valid-time intervals.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;Databricks marks bitemporal storage as Beta. This run validates five tiny rows and does not test load. I would evaluate it when valid time differs from ingest time and consumers need as-of-system-time queries.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;The full result map&lt;/H2&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="All 18 measured configurations grouped by handled, configuration-dependent, business-semantics, and ambiguous-order outcomes." style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/30630i34D53099C293954C/image-size/large?v=v2&amp;amp;px=999" role="button" title="summary_matrix.png" alt="All 18 measured configurations grouped by handled, configuration-dependent, business-semantics, and ambiguous-order outcomes." /&gt;&lt;span class="lia-inline-image-caption" onclick="event.preventDefault();"&gt;All 18 measured configurations grouped by handled, configuration-dependent, business-semantics, and ambiguous-order outcomes.&lt;/span&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;EM&gt;All 18 measured configurations grouped by production outcome.&lt;/EM&gt;&lt;/P&gt;&lt;P&gt;You can inspect the machine-readable matrix in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/normalized/summary_matrix.json" target="_blank" rel="noopener"&gt;results/normalized/summary_matrix.json&lt;/A&gt; and the captured rows in &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/results/raw/target_state.json" target="_blank" rel="noopener"&gt;results/raw/target_state.json&lt;/A&gt;.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;My production checklist&lt;/H2&gt;&lt;OL&gt;&lt;LI&gt;&lt;STRONG&gt;Name the business clock.&lt;/STRONG&gt; Put the column that defines recency in SEQUENCE BY.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Search for ties per key.&lt;/STRONG&gt; Add a source-side tie-breaker when two states can share one sequence value.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Write down NULL semantics.&lt;/STRONG&gt; Use IGNORE NULL UPDATES only when NULL means absent.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Choose the state model.&lt;/STRONG&gt; SCD1 keeps current state; SCD2 keeps ordered history.&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Filter history noise.&lt;/STRONG&gt; Exclude sync metadata unless it represents a business event.&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;Add a target-state predicate for each rule: update status checks execution, while the predicate checks the expected target state.&lt;/P&gt;&lt;HR /&gt;&lt;H2&gt;Reproduce the run&lt;/H2&gt;&lt;PRE&gt;git clone https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test.git
cd lakeflow-auto-cdc-torture-test
python -m pip install -e ".[dev]"
databricks auth login --host https://&amp;lt;workspace-url&amp;gt; --profile DEFAULT
make setup
make test
make results&lt;/PRE&gt;&lt;P&gt;You need Python 3.10 or newer, GNU Make, jq, the Databricks CLI, and access to a Databricks workspace. The &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/blob/main/docs/reproduction.md" target="_blank" rel="noopener"&gt;reproduction guide&lt;/A&gt; covers the one-time setup and evidence capture.&lt;/P&gt;&lt;P&gt;Sixteen targets matched their baselines after update 2. The two SCD2 targets that received late history changed as expected. The verifier checked all 18 targets and produced 10 HANDLED, 3 CONFIGURATION_DEPENDENT, 3 BUSINESS_SEMANTICS, and 2 AMBIGUOUS_ORDER results.&lt;/P&gt;&lt;P&gt;The checked-in result set was captured on 2026-09-01 from clean commit 01d53b4; target_state.json records the pipeline and both update IDs.&lt;/P&gt;&lt;H2&gt;Scope&lt;/H2&gt;&lt;P&gt;This suite uses tiny controlled datasets. I did not test throughput, backpressure, or large joins. Each flow read one isolated source table; I did not test joins or interactions across streams. The delete-and-late-event case stayed inside the configured 48-hour tombstone-retention window. Bitemporal load behavior also sits outside this run.&lt;/P&gt;&lt;P&gt;The evidence comes from one workspace, one SQL warehouse, one customer key, serverless Advanced-edition compute on the CURRENT channel, and tiny deterministic inputs. Use separate tests for throughput, schema evolution, and multi-stream joins.&lt;/P&gt;&lt;H2&gt;Bring your failure mode&lt;/H2&gt;&lt;P&gt;Clone the repository and replace one generator with an event sequence from your source. Add the expected target state, run both phases, and compare the capture.&lt;/P&gt;&lt;P&gt;If the suite misses your case, &lt;A href="https://github.com/ivanvyd/lakeflow-auto-cdc-torture-test/issues/new" target="_blank" rel="noopener"&gt;open an issue&lt;/A&gt; with the source rows, SEQUENCE BY expression, storage type, expected target, and observed target. I’m happy to turn a clear failure report into another reproducible scenario.&lt;/P&gt;</description>
      <pubDate>Wed, 02 Sep 2026 01:54:46 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/all-18-lakeflow-auto-cdc-configurations-went-green-five-failed/m-p/167235#M1520</guid>
      <dc:creator>ivanvyd</dc:creator>
      <dc:date>2026-09-02T01:54:46Z</dc:date>
    </item>
  </channel>
</rss>

