Streamhouse · STH-06
Streaming data quality — in-flight DQ, quarantine / DLQ, completeness
- 4 h
- requires STH-05
From your world. dbt tests and SOX controls: not-null, accepted-values and relationship tests, but applied in-flight with a quarantine instead of failing the run
In-flight DQ + quarantine/DLQ on the streaming orders pipeline
Add a data-quality stage in front of the STH-05 star schema. Valid orders flow on to fct_orders
(the as-of join). The pipeline routes invalid orders to a quarantine table with the reason they
failed. It never silently drops them. The acceptance gate is completeness. Every input order
lands in exactly one place: the input of fct_orders or the quarantine.
Implement dq_routing.py with two functions that return SQL query bodies. The test provides the
Paimon catalog, the orders_raw + customers source tables, the good_orders / quarantine
target tables, the SCD2 dimension and the fact build.
def good_orders_select(raw: str, cust: str) -> str: ...
def quarantine_select(raw: str, cust: str) -> str: ...
The data contract (three rules)
For each raw order (oid, cid, amt, ots):
- not-null customer —
cid IS NOT NULL. Elsereason = 'null_customer'. - positive amount —
amt IS NOT NULL AND amt > 0. Elsereason = 'non_positive_amount'. - valid customer reference —
cidmust exist incustomers. Elsereason = 'unknown_customer'.
An order is good if all three rules pass. Otherwise it is quarantined.
good_orders_select(raw, cust)
Return a SELECT oid, cid, amt, ots of the orders that pass all rules. Join raw to cust, so
that only referenced customers remain. Require a non-null positive amount.
quarantine_select(raw, cust)
Return a SELECT oid, reason of the orders that fail any rule. Use a LEFT JOIN to cust to
detect the referential miss. Use a CASE to assign exactly one reason in this priority order:
null_customer, then non_positive_amount, then unknown_customer. The WHERE must be the exact
complement of the good predicate.
What the tests check
- Completeness (the test that catches a silent drop):
count(good_orders) + count(quarantine) == count(orders_raw), and the twooidsets are disjoint. Suppose aWHEREfilters bad rows without routing them to the quarantine. This breaks the check, and the test fails. - Reconciliation (G6):
good_ordersequals an independent DuckDB computation of the valid set.quarantineequals the independent invalid set with the correct reasons. - The mart stays correct:
fct_ordersbuilt on the good orders (the STH-05 as-of join) matches the DuckDB as-of join over only the valid orders. - Mutants fail if they silently drop rows, overlap the two predicates, or mislabel reasons.
Everything runs with no Docker on the embedded Flink SQL (paimon_sql) runner.
Starter code
"""Starter — right now this FILTERS bad orders out of the mart but never routes
them anywhere, and the quarantine is left empty. So count(good)+count(quarantine)
!= count(input) and the quarantine does not match the invalid set (G1 fails)
until you route every failing order to the quarantine with its reason. See README.
"""
def good_orders_select(raw: str, cust: str) -> str:
return (
"SELECT o.oid, o.cid, o.amt, o.ots "
f"FROM {raw} o JOIN {cust} c ON o.cid = c.cid "
"WHERE o.cid IS NOT NULL AND o.amt IS NOT NULL AND o.amt > 0"
)
def quarantine_select(raw: str, cust: str) -> str:
# TODO: route EVERY order that fails a rule here, with a reason. Right now it
# quarantines nothing, so the bad rows are silently dropped from the mart.
return (
"SELECT o.oid, CAST(NULL AS STRING) AS reason "
f"FROM {raw} o WHERE 1 = 0"
)
Hints
Hint 1
Split, don't filter. The good predicate requires ALL rules to pass; the quarantine predicate requires ANY rule to fail. Make them exact complements so every order goes to exactly one table — then count(good)+count(quarantine) == count(input).
Hint 2
The referential rule is a LEFT JOIN to customers: a row whose matching customer id is NULL after the join fails the 'unknown_customer' rule. The not-null and positive-amount rules are plain predicates on the order columns.
Hint 3
Report ONE reason per quarantined row with a CASE (priority order: null_customer, then non_positive_amount, then unknown_customer). The reason label depends on CASE order, but membership (good vs quarantine) must not — a bad row appears exactly once.
uv run de start STH-06/01-dq-quarantine
# edit workspace/STH-06/01-dq-quarantine/
uv run de test