CDC Lakehouse Pipeline — E-commerce Orders (Kafka + Spark + DuckDB) - #15
Open
Shabbirsheikh wants to merge 2 commits into
Open
CDC Lakehouse Pipeline — E-commerce Orders (Kafka + Spark + DuckDB)#15Shabbirsheikh wants to merge 2 commits into
Shabbirsheikh wants to merge 2 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A CDC pipeline for an e-commerce order-management domain (
customers,orders,order_items,payments) that keeps a Parquet lake and aDuckDB warehouse synchronized via Kafka + Spark Structured Streaming.
Full design rationale, trade-offs, and what's been verified live are in
submission/Shabbirsheikh/DESIGN.md— this description is the condensed version.
Source Schema Design
customers,orders— independent identity/lifecycle.order_items(lifecycle entirely tied to its order),payments(tied to an order but looser — can arrive/retry after ordercreation, so it carries its own status/timestamps).
orders.customer_idis areal FK to
customers;order_items.order_id/payments.order_idaredeliberately not DB-level FKs — a real DuckDB 1.5.5 engine limitation
(confirmed via an isolated repro) prevents
UPDATEing a table that'ssimultaneously an FK child and FK parent, and
orders.statuschangesconstantly. That referential link is enforced in the data-quality layer
instead. Indexes on every FK-shaped lookup column.
amounts, order total reconciling with line items, payment timing,
legal status-transition state machines — full list in
source.md.CDC Strategy
SourceWriterwrites real SQL tosource.duckdbfirst,then — only after that succeeds — publishes to Kafka. This is
application-level dual-write (simulated CDC), since DuckDB has no
WAL/logical-replication slot to tail. Known risk: a crash between the
two writes loses that event; a real Postgres source would use Debezium
tailing the WAL instead.
checkpointLocationtracks processed Kafka offsets. I haven't run a dedicated
kill-and-restart test against this exact pipeline, so I'm flagging that
as reliance on a well-established framework guarantee rather than
something I've independently proven here.
WHERE excluded._cdc_seq > current._cdc_seq, so a duplicate orout-of-order event can never overwrite a row already ahead of it. The
lake itself isn't strictly exactly-once at the storage layer (a retried
Spark batch can duplicate a Parquet row), but that's harmless for
warehouse correctness.
soft-deletes (
_deleted = TRUE) rather than removing the row.Lake and Warehouse Modeling
(
data/lake_parquet/event_date=YYYY-MM-DD/), written by Spark's nativestreaming sink. Nothing here is ever updated or deleted.
_cdc_seqand
_deleted. No independent write path — always a function of "whichlake events have been applied," via one function,
apply_lake_events().reconstruct_as_of(lake_rows_up_to_bound)replays acaller-bounded event list into a throwaway in-memory warehouse, never
touching the live one. Verified live: for an order that moved
pending -> paid -> shippedacross three real Kafka offsets, boundingthe replay to each earlier offset correctly reproduced
pendingandpaidwhile the live warehouse correctly showedshipped.Schema Change Safety
check_schema_contracts.pydiffs the live source schemaagainst an expected-columns/types contract.
type) raises
SchemaContractViolation; ingestion should stop ratherthan write against a wrong assumption.
additive column is a warning, not a failure. (Known gap: nothing
automated currently watches for that exit code — see Limitations.)
Validation Parity
(including the link DuckDB itself couldn't enforce), enum domain.
payment timing, and status-transition legality (needs the lake's full
history per entity, not just the warehouse's latest state).
DataQualityViolationlists every failingcategory and row; non-zero exit, same pattern as the schema check.
Catalog Exposure
catalog/catalog.jsonregisters 5 datasets (the lake plus four warehousetables), each with owner, intended consumers, update cadence, and full
schema.
validate_catalog.pyenforces every dataset has a complete entryand that documented schemas haven't drifted from the code. A consumer
discovers datasets by reading
catalog.jsonand queries the lake viaread_parquet(...)or the warehouse by connecting directly towarehouse.duckdb.Validation
Full setup/run instructions:
submission/Shabbirsheikh/README.md.I've run every command above against this exact code: 21/21 tests pass,
schema contract passes, data quality checks pass with zero violations,
catalog validation passes.
Known Limitations / Next Steps
assignment's testing requirements call for it directly.
Responsible AI Usage
I used Claude during implementation to speed up writing boilerplate and
draft code. The domain choice, the build order, and the trade-offs
(DuckDB over Postgres, on-demand as the default run mode) were my
decisions. I personally ran and verified the infrastructure at every
stage — records landing in Kafka/lake/warehouse, continuous mode picking
up a new record live, and the time-travel reconstruction documented
above. The DuckDB FK limitation was confirmed with an isolated repro
before I accepted it as a real engine bug rather than a schema mistake.
Author Checklist