Split a batch into quarantine and publish
Build the pipeline-dq-quarantine starter the way the Quarantine then Publish pattern prescribes: a quality contract splits each batch into a published set and a quarantine with reasons. You finish with a test that proves no row is ever lost and an alert on the quarantine rate.
At a glance
| When | External/partner data or any table with an SLA; High-value downstream metrics that one bad batch could poison |
|---|---|
| Where | Data Quality |
| Why | A row never disappears — it is either published or quarantined with a reason, so one bad batch cannot poison the table. |
Learning objectives
- Express a quality contract as executable checks
- Design a quarantine table with a reason
- Alert on the quarantine rate, not just on failure
What you need
- The pipeline-dq-quarantine starter (contract/orders.yml, src/validate.py, src/publish.py)
- DuckDB or PySpark to compile the contract rules to SQL
- A batch with ~5% deliberately invalid rows (nulls, dupes, out-of-range)
- [object Object]
Steps
- Split every batch into publish and quarantine on a quality contract. (from pattern-quarantine-publish )
- Validate the contract offline against fixtures with `make test`, then run `make run`. (from project-pipeline-dq-quarantine )
- Load the contract and compile rules to SQL (DuckDB) or Spark expressions. (from project-pipeline-dq-quarantine )
- Split into pass/fail with a stable `failure_reason`. (from project-pipeline-dq-quarantine )
- Publish the pass set; write the fail set to quarantine with provenance. (from project-pipeline-dq-quarantine )
- Alert when the quarantine rate > 1% for 2 consecutive runs. (from project-pipeline-dq-quarantine )
Check yourself
- Every row is either published or quarantined — never lost; the quarantine contains exactly the seeded bad rows, and a clean batch produces an empty quarantine.
- A quarantine rate above 1% for 2 consecutive runs fires the alert with the failure_reason breakdown attached.
Worked example
Split every batch — pass publishes, fail quarantines with a reason — from pattern-quarantine-publish
Split every batch into publish and quarantine on a quality contract.
pass ─▶ published table (SLA-bearing)
fail ─▶ quarantine table (retained, alertable, reprocessable)
A row never disappears: it is either published or quarantined with a reason.
Alert on quarantine *rate*, not just on a hard failure — a rising rate is the
early signal of an upstream problem.
Starter layout — contract, validate, publish — from project-pipeline-dq-quarantine
pipeline-dq-quarantine/
contract/orders.yml # rules: not_null, unique, range, enum
src/validate.py
src/publish.py
tests/test_quarantine.py
Validate the contract offline, then run the batch — from project-pipeline-dq-quarantine
make test # validates against the fixtures offline
make run
Sourced from
Security scan
Last sweep 2026-10-24: 0 finding(s), 0 marked as examples.
Common gotchas
- A quarantine nobody reviews is just a slower data loss.
- Contract too strict → quarantine rate explodes; tier rules and loosen with evidence.
- Alert on quarantine rate, not just on a hard failure — a rising rate is the early signal of an upstream problem.
Production considerations
- Alert when quarantine rate > 1% for 2 consecutive runs.
- Ship a reprocess command that re-runs the quarantined rows after a fix.
Diagram
batch
|
|- [contract check] --> pass --> published/ (SLA-bearing)
|
|- [contract check] --> fail --> quarantine/ (+reason, +source_file)
|
|- quarantine rate > 1% x2 runs --> alert --> review + reprocess
Registry stats
| Verification | community · Oct 21, 2026 |
|---|---|
| Completeness | 81 |
| Trust multiplier | ×1.25 |
| Composite | 34.15 · adequate |
| Freshness | 3 days since last source check |
| Difficulty | intermediate |
| Version | 0.1.0 |
| License | CC-BY-4.0 |
| Security flags | none |
Did this solve your problem?
Split a batch into quarantine and publish