client based

Streaming Data Pipeline — LIVE Scenario

A complete streaming data platform demonstrated through the simplest story everyone understands — a Mumbai cloud kitchen. Customer orders → kitchen preparation → food delivery, with every step an event flowing in real time through a production-shaped stack: SQLite with a transactional outbox, a Redpanda Connect outbox relay, Redpanda as the event log, a MinIO bronze data lake, dbt + DuckDB marts, and live auto-refreshing dashboards. Python powers the pipeline, TypeScript the dashboard, SQL (dbt) the warehouse — and there is no JVM anywhere in the stack.

This is a live, running pipeline — watch orders stream through it in real time. The demo lives exactly one business day: data is wiped every midnight and the kitchen reopens fresh.

stream.steleios.com ↗
RedpandaRedpanda Connectdbt + DuckDBReal-time

What this project sets out to do

Make streaming architecture legible — and prove it behaves like production. Every core streaming concept — topics, partitions, consumer groups, offsets, delivery semantics, idempotency, dead-letter queues, change data capture, replay, backpressure, event time vs processing time, schema drift — is not explained on a slide but demonstrated live in a running system. And the system earns the word production-shaped: no dual writes, no lost sale, effectively exactly-once processing, and 279 automated checks behind it — including hostile-input and schema-drift drills fired at the live pipeline, two of which found real bugs that are now fixed and pinned by tests.

Feature Highlights

No dual writes — outbox + relay

Every state change commits the business rows and the event into a transactional outbox in one SQLite transaction; a Redpanda Connect relay tails that outbox and is the only thing that publishes. If the transaction rolls back, neither exists — the database and the stream can never diverge.

Redpanda as the system of record

Four topics, three partitions keyed by order id — per-order ordering with parallel scale-out — on infinite retention and a persistent volume. Idempotent producers (acks=all) plus ack-after-work offset commits give at-least-once delivery; idempotent processing at every layer makes it effectively exactly-once. No sale is ever lost to a crash.

The whole stack, without a JVM

Redpanda is a single C++ binary with its own Raft — no broker JVM, no ZooKeeper — and both integration hops are small Go pipelines rather than a Connect cluster running plugins. Measured on the live 2-vCPU VM under a full order feed, the entire streaming tier runs at roughly 3% CPU and 352 MB, against the ~1.7 GB of JVM heap the equivalent Kafka, Kafka Connect and MySQL tier was configured to reserve.

Bronze lake to tested marts

A second Redpanda Connect pipeline sinks every event as date-partitioned raw JSON into MinIO — the bronze layer nothing can delete — and dbt on DuckDB builds order facts, item profitability, and per-minute demand, with in-warehouse tests asserting the profit equation, the delivery physics, and the fulfillment rules on every run.

Live operations dashboards

A fully typed Bun backend reads the same stream with its own consumer group and rebuilds complete state from offset zero on every boot; the Vue 3 + Pinia frontend serves owner analytics, kitchen and delivery ops boards, per-order live customer tracking, and an append-only audit log — auto-refreshing every two seconds.

Tested by attacking it

The relay was killed mid-stream with its cursor at 1,331: twelve orders placed while it was dead kept committing to SQLite, and on restart exactly those twelve published — no loss, no duplicates. Malformed bytes, unknown event types and business-rule violations fired at the live topics are dead-lettered with reasons, never dropped and never crashing a consumer.

279 automated checks

180 Python unit tests with passing and failing cases for every business rule — the repository suite running against a real SQLite file, so schema constraints, rollback, WAL mode and concurrent writers are genuinely exercised — plus 41 TypeScript dashboard tests, 36 dbt tests in-warehouse, and 22 live end-to-end edge-case checks covering hostile input, schema drift and an API that fails closed. There is no database account to leak: the OLTP store is a file, and the relay opens it read-only.

The pipeline, live · every dot is an order event; the callouts note the complexity handled at each hop

Customer orders SQLite OLTP + outbox RP Connect outbox relay Redpanda the event log Kitchen svc Python · 8 stations Delivery svc 15–25 km/h riders RP Connect bronze sink MinIO bronze lake (S3) dbt dbt + DuckDB marts + tests Live dashboard Bun + Vue 3 + Pinia order events confirmed → prep → ready ALL topics stream in events back to the log no dual-write: rows + event commit in ONE transaction (outbox); the relay publishes it — DB and stream can never diverge replayable log · infinite retention · persistent volume · keyed by order id · malformed ⇒ dead-letter + audit log at-least-once, idempotent; offsets commit only after the work — a crash replays the order, never loses the sale immutable raw history · staging dedupes by event id · dbt tests assert profit equation + delivery physics, in-warehouse everything arrives via Redpanda: the dashboard's own consumer group reads the orders + kitchen + delivery (+ dead-letter) topics from the very beginning · out-of-order safe (status folds monotonically) · full state rebuilt on every boot validation on EVERY state change: guarded SQL transitions (rowcount-checked) · hostile-input parsing at every consumer boundary · money = integer paise only

Production failure cases, handled by design

A streaming pipeline is defined by what happens when things go wrong — a crashed consumer, a duplicated message, a stalled relay, a hostile payload, a contract that moves underneath you. Every case below is handled in the running system, and most were proven by firing the failure at it rather than assumed.

  • Dual-write problem — state changes commit business rows and the event into the outbox in one SQLite transaction; the Redpanda Connect relay is the only thing that publishes them. The database and the stream cannot disagree.
  • Relay crash mid-batch — the cursor advances only after Redpanda acknowledges the whole batch, so a crash replays it rather than skipping it. Proven live: killed at cursor 1,331, twelve orders placed while it was dead, exactly twelve published on restart.
  • Service crash mid-order — consumers commit offsets only after the work finishes, via a per-partition ack watermark. A crashed kitchen or delivery service replays its unfinished orders on restart instead of skipping them.
  • Duplicate events (at-least-once) — every layer is idempotent: services skip seen order ids, the dashboard dedupes by event id, dbt staging keeps exactly one row per event id.
  • Out-of-order arrival — cross-topic ordering isn't guaranteed, so the dashboard folds events monotonically: a late event can never move an order's status backwards.
  • Malformed / hostile payloads — broker messages are treated as untrusted input and validated on parse. Failures go to the dead-letter topic with the reason in headers, are audit-logged, and never crash a consumer — fail closed, per message.
  • Schema drift — deliberately asymmetric, because the three directions carry different risk. A new field added by a newer producer is ignored; a missing optional field reads as unset; a changed schema version is rejected to the dead-letter queue rather than half-parsed. Firing all three at the live pipeline is what caught a real bug: the Python consumers enforced the version but the dashboard did not, so an incompatible event was quarantined by one and silently accepted by the other. All three boundaries now agree.
  • Poison message in the data lake — bronze is newline-delimited JSON, so one unparseable line makes an entire file unreadable and can take down a whole warehouse build. The sink groups each batch by destination and routes unparseable payloads to a quarantine prefix; the warehouse then re-validates the envelope itself and routes rejects to a dedicated table, so "the facts are clean" never quietly means "some data vanished".
  • Illegal state transitions — guarded SQL verified by rowcount: a closed order cannot be re-closed, an abandoned one cannot confirm. Validation on every state change, not just at intake.
  • Money correctness — all money is integer paise, no floats. dbt tests assert profit = price − ingredient cost − 5% GST − driver fee in-warehouse, on every run.
  • Lost customers — ~10% of placed orders abandon, and that's a first-class event tracked in the funnel and the owner's abandonment rate — never silently missing.
  • Failures & retries visibility — consumer crashes, retries, and timeouts are recorded to the audit log and persisted append-only as daily JSONL. Silence is never mistaken for success.
  • Bad configuration — settings fail closed: malformed or out-of-range values raise on startup instead of falling back silently. Secrets live in gitignored env files and are injected as environment variables, never embedded in the pipeline definitions.

Beyond the failure cases, the live homepage walks through the full vocabulary of streaming — topics and partitions, consumer groups, offsets and commits, delivery semantics, DLQs, change data capture, replay, retention, backpressure, event time vs processing time, compression on the wire, fan-out, stream–table duality, the medallion architecture — each one pointing at where it is happening in the running pipeline.

✦ Want something like this for your business? Talk to us