Two numbers for the same sale
TiffinGo runs its biggest-ever Diwali dinner sale. The CEO wants a live revenue counter on the office TV all evening. Kabir builds it on the streaming path, and at midnight it proudly shows ₹2.31 crore. Next morning, the finance team's batch report says ₹2.24 crore. The CEO asks one question: “Which number is true?”
Sir, my live counter didn't subtract refunds properly, and some orders came twice when phones retried. But it was live! Finance took six hours.
Both numbers are useful, Kabir. The live one answers “how is the sale going right now?” The batch one answers “what exactly did we earn?” In 2011, an engineer called Nathan Marz proposed running both paths together and merging them. He called it the Lambda architecture.
Then everyone is happy. What's the catch?
You now maintain the same business logic twice: once in the stream code, once in the batch code. Fix the refund rule in one and forget the other, and the numbers disagree forever.
Can't we just have one path?
That's the Kappa architecture, proposed in 2014 by Jay Kreps, one of Kafka's creators: only the streaming path, and when the logic changes, replay the saved event log through the new code. Today you'll build both in Python and decide which one TiffinGo should use.
What are the Lambda and Kappa architectures?
The Lambda architecture processes data through two parallel paths: a batch layer that stores the complete, immutable master dataset and periodically recomputes accurate batch views, and a speed layer that processes only recent data in real time to produce low-latency real-time views. A serving layer answers queries by merging both views. The Kappa architecture removes the batch layer: all data is treated as a stream stored in a replayable, append-only log, a single stream-processing code path produces the views, and reprocessing is done by replaying the log through a new version of the job.
Lambda: a fast, rough answer from the stream, plus a slow, exact answer from batch; show both combined. Kappa: one stream-only path; if you need to fix or recompute something, rewind the saved event log and run it again.
Words you'll meet in this lesson
The two architectures at a glance
| Lambda | Batch layer (Spark/Hadoop on the lake) + speed layer (Flink/Spark Streaming) + serving layer · accurate and fresh · two codebases |
|---|---|
| Kappa | Kafka log (long retention) + one stream processor + serving store · one codebase · reprocess by replaying the log |
Lambda and Kappa at three levels
Beginner Two cooks, or one cook with a recipe book
- Lambda is a kitchen with two cooks. The fast cook sends a quick tasting plate the moment an order arrives. The head chef cooks the full, perfect meal more slowly. The waiter serves the perfect meal when it's ready and the tasting plate until then.
- Kappa is one very good cook who keeps every ingredient delivery and every recipe. If a recipe was wrong, the cook simply re-cooks from the saved ingredients with the corrected recipe.
- Lambda gives you two opinions to reconcile. Kappa gives you one opinion that can be redone.
Intermediate Layers, merging and replay
- Batch layer: appends every event to an immutable master dataset in the lake and recomputes views from scratch (for example nightly with Spark). Recomputing from raw data corrects any earlier mistakes.
- Speed layer: processes only events that arrived after the last batch run, with a stream processor. Its results are temporary: they are discarded once the next batch view covers that period.
- Serving layer: stores views (Cassandra, HBase, Druid, a warehouse) and answers
query = batch view + real-time view. - Kappa: Kafka keeps events long enough to replay. To change logic, start job v2 from offset 0 writing to a new table, wait until it catches up with the live stream, switch readers to the new table, then stop v1.
Advanced Trade-offs in real systems
- Lambda's cost is duplicated logic: two implementations of every metric, two test suites, and subtle drift between them. Its strength is that batch recomputation from the lake is simple, robust and can use any batch tool.
- Kappa's cost is in the stream: long log retention (tiered storage helps), replays that may take hours for large state, careful exactly-once and idempotent sinks, and schema evolution across years of events.
- Unified engines (Apache Flink, Spark Structured Streaming, Apache Beam) let one codebase run in batch and streaming modes, removing much of Lambda's duplication.
- Lakehouse streaming tables (Delta, Iceberg, Hudi) can be both a streaming sink and a batch source, so many modern platforms use a pragmatic mix: a stream for live views plus scheduled recomputation from the same tables and the same code.
- Choose by need: how often must you recompute history, how complex is the logic, how long can the log be kept, and how strong is the team in stream processing?
Exam tip: Draw Lambda with its three layers (batch, speed, serving) and arrows from one data source to both batch and speed. Draw Kappa as log → stream processor → serving, with a “replay” arrow. Then give two advantages and two disadvantages of each.
Lambda vs Kappa side by side
| Lambda | Kappa | |
|---|---|---|
| Processing paths | Two: batch + speed | One: stream only |
| Source of truth | Immutable master dataset in the lake | Replayable event log (Kafka with long retention) |
| Codebases | Two, must stay consistent | One |
| Fixing a bug / new metric | Change both; the next batch run recomputes history | Deploy a new job version and replay the log |
| Accuracy | Batch view is exact; speed view approximate until replaced | Exact if the stream job is exactly-once |
| Complexity | Operational: two systems to run | Technical: stateful streaming, replays, retention |
| Good fit | Heavy historical recomputation, ML training, very complex batch logic | Event-driven businesses, logic expressible as stream jobs, frequent small changes |
Typical tools in each layer
Batch layer
HDFS or S3 for the master dataset; Spark (earlier Hadoop MapReduce) to recompute views; Airflow to schedule.
AccurateSpeed layer
Kafka in front; Flink, Spark Structured Streaming or Kafka Streams (earlier Apache Storm) to keep real-time views.
FreshServing layer
Cassandra, HBase, Apache Druid, Apache Pinot or a warehouse, holding views that dashboards and APIs query.
AnswersThe “lambda” name comes from the shape of the diagram (data splitting into two branches), not from Python's lambda functions or AWS Lambda. Don't mix them up in interviews.
A query during the sale, the Lambda way
Every event is appended, twice over
The event goes into Kafka. From there, one consumer writes it to the master dataset in the lake (batch layer), and another feeds the speed layer.{"offset": 50213, "order": "O90017", "amount": 499, "type": "sale", "ts": "19:42:05"}The batch view from last night
A nightly Spark job recomputed revenue from all raw events up to midnight: duplicates removed, refunds subtracted, late events included. Exact, but it knows nothing about today.batch_view = {"revenue_till_yesterday": 18_40_00_000} # computed at 02:00 by SparkThe speed layer covers today
The stream processor keeps a running total for events after midnight only. Quick, but approximate: a duplicate or a late refund may slip through until tomorrow's batch.speed_view = {"revenue_today": 0} for e in todays_events: # updated within seconds of each event speed_view["revenue_today"] += e["amount"] if e["type"] == "sale" else -e["amount"]The serving layer merges
The dashboard asks the serving layer; it adds the exact past to the fresh present. The CEO sees a number that is both complete and up to the minute.batch_view = 18_40_00_000 speed_view = 2_31_00_000 print("Revenue (this season):", batch_view + speed_view)Revenue (this season): 207100000Next night, batch catches up
At 02:00 the batch job recomputes everything including the sale day: ₹2.24 crore for the day, not ₹2.31 crore.
The new batch view replaces the speed layer's estimate for that day, and the speed layer starts again from zero for the new day. Errors in the fast path never last longer than one batch cycle. That is Lambda's safety net.
Sale night, the morning after, and the week Kabir switched to Kappa
Step through six frames. Left: what the CEO and finance see. Right: which layer is doing the work.
- 18:00: the sale begins. The live counter starts at zero.
- 20:30: ₹1.12 crore, updated seconds after each order.
- The season total shown is yesterday's exact figure plus today's live figure.
- Morning after: finance explains the difference: duplicates and late refunds removed, late orders added. Exact: ₹2.24 crore.
- A week later: a business rule changed in the batch code only. The two paths now disagree every day.
- After moving to Kappa, one job produces both numbers, and rule changes are applied by replaying the log.
- Every order event goes from Kafka into both the speed layer and the batch layer's master dataset.
- The speed layer keeps today's running total, updated per event. The batch layer only stores raw events for now.
- The serving layer answers the dashboard with batch view (till yesterday, exact) + real-time view (today, live).
- At 02:00 the batch layer recomputes from all raw events. Its exact figure replaces the speed layer's estimate, which resets to zero.
- The weakness: the same metric exists in two codebases. One was updated, the other wasn't.
- Kappa: a single stream job. For a rule change, job v2 replays the retained log from offset 0 into a new serving table; once it catches up, readers switch and v1 stops.
Real-time scenarios with both architectures
Watch the live estimate and the batch correction on one chart, replay a Kafka log after a bug, and (advanced) walk the decision an architect makes when choosing.
Real-time scenario 1Sale night on one chart: speed layer vs batch correction
Revenue per hour during the sale. The green line is what the speed layer showed live; the blue line is what the batch layer computed afterwards from complete data.
- 18:00–19:00: the green line is drawn live by the speed layer as orders arrive.
- Peak hour: managers use the live line to add riders in busy areas, a decision that can't wait until tomorrow.
- 23:00: the sale ends. The live total is ₹2.31 crore.
- 02:00: the batch layer recomputes every hour from all raw events. The blue line is slightly lower each hour.
- The difference is about 3%. Lambda shows both: the live line during the night, the exact line from the morning. (Numbers illustrative.)
Kappa in action: replaying a month after a bug
TiffinGo moved to Kappa. On 19 October, a bug is found in how coupons were applied since 20 September. The log keeps 30 days. Watch the team fix a month of numbers without any batch system.
- The bug: coupons were applied twice for a month. Job v1 keeps the live dashboards running meanwhile.
- 11:00: the fixed job v2 starts reading from offset 0 (20 September), writing to a new table.
- 13:30: v2 replays historical events many times faster than real time, so its lag shrinks quickly.
- 16:10: v2 has caught up with the live end of the log. The team compares outputs and points dashboards to the new table.
- 16:20: v1 is stopped. A month of history was corrected with the same single codebase. (Times illustrative.)
The architect's decision: Lambda, Kappa or a unified engine?
Three teams at TiffinGo ask for real-time plus historical data. Watch the architect walk each one through the same questions and reach different answers.
- Q1: payroll needs no real-time answer, so it stays a simple batch job. Don't build Lambda or Kappa without a real-time need.
- Q2: retraining the delivery-time model needs heavy historical joins best done in batch, while live predictions use a stream: a Lambda-style design.
- Q3: order metrics are simple stream logic, and the log can keep enough history to replay: Kappa fits.
- Q4: if the engine can run the same code in batch and streaming modes, much of Lambda's duplicate-code problem disappears.
- There is no universal winner. Architects choose per use case, and modern platforms often blend both ideas.
Where these patterns appear
1. Online advertising: Lambda-style billing
Ad clicks are counted in real time to stop campaigns that hit their budget, while invoices are produced by batch jobs that remove fraudulent and duplicate clicks from the complete data.
2. Banking ledgers: event logs as the source of truth
Many modern banking and wallet systems record every transaction as an immutable event and derive balances and statements from the log. Replaying events to rebuild state is close to the Kappa idea.
3. Machine learning features: batch + streaming
Recommendation systems combine long-term features computed in batch (a user's favourite cuisine over months) with fresh streaming features (what they clicked a minute ago) when scoring.
4. Live dashboards with nightly correction
Sales, delivery and operations dashboards often show stream numbers during the day and switch to batch-verified numbers for previous days: a simple, practical Lambda.
5. IoT fleets: unified stream and batch
Vehicle and machine telemetry is processed live for alerts, and the same pipeline code is re-run over stored history to test new alert rules before switching them on.
Simplified patterns; real systems often blend Lambda and Kappa ideas.
Lambda in miniature: batch view + speed view
An append-only list of events plays the master dataset. Last night's batch job computed an exact view up to a cut-off; the speed layer keeps a running total for everything after it. The query adds the two. Then, in the challenges, replay the log Kappa-style, watch two codebases drift, and size a log for replay.
# The master dataset: every event ever, append-only (sale = +, refund = -)
master = [("O1", 120, "sale"), ("O2", 250, "sale"), ("O3", 90, "sale"),
("O2", 250, "refund"), ("O4", 300, "sale"), # up to last night
("O5", 140, "sale"), ("O6", 60, "sale")] # arrived today
def revenue(events):
total = 0
for order, amount, kind in events:
total += -amount if kind == "refund" else amount
return total
CUTOFF = 5 # last night's batch job processed events 0-4
batch_view = revenue(master[:CUTOFF]) # BATCH LAYER: exact, recomputed nightly
speed_view = revenue(master[CUTOFF:]) # SPEED LAYER: only events since the batch run
print("Batch view (till last night):", batch_view)
print("Speed view (today, live) :", speed_view)
print("Query answer = batch + speed:", batch_view + speed_view)
# A new event arrives: only the speed view changes, instantly
master.append(("O4", 300, "refund"))
speed_view = revenue(master[CUTOFF:])
print("After a refund arrives :", batch_view + speed_view)master = [...]The master dataset is only ever appended to. Corrections (like a refund) are new events, not edits.batch_view = revenue(master[:CUTOFF])The batch layer recomputes from all raw events up to the cut-off. Accurate, but only as fresh as its last run.speed_view = revenue(master[CUTOFF:])The speed layer handles only the recent tail, so it can update within seconds.batch_view + speed_viewThe serving layer's job: merge the two views into one answer.
Run it on your PC: a real log file, replayed
This program writes an append-only log file (orders.log), one JSON event per line with an offset, just like a Kafka partition. It then shows Kappa (a buggy v1 job, then a fixed v2 replayed from offset 0) and Lambda (batch view up to offset 4 plus speed view after it). Save it as replay_log.py, run it, and open orders.log to see the events.
Kappa replay and Lambda merge on one log
import json, os
# ---------- An append-only event log, like a Kafka topic ----------
LOG = "orders.log"
raw = [("O1", 120, "sale"), ("O2", 250, "sale"), ("O3", 90, "sale"), ("O2", 250, "refund"),
("O4", 300, "sale"), ("O5", 140, "sale"), ("O6", 60, "sale"), ("O4", 300, "refund")]
with open(LOG, "w") as f:
for offset, (oid, amount, kind) in enumerate(raw):
f.write(json.dumps({"offset": offset, "order": oid, "amount": amount, "type": kind}) + "\n")
print(f"Log written: {len(raw)} events (offsets 0-{len(raw) - 1}), never edited afterwards\n")
def consume(logic, start=0, end=None):
"""Read the log from an offset, like a consumer reading a Kafka partition."""
total = 0
with open(LOG) as f:
for line in f:
e = json.loads(line)
if e["offset"] < start or (end is not None and e["offset"] >= end):
continue
total = logic(total, e)
return total
def v1(total, e): # BUG: treats refunds as sales
return total + e["amount"]
def v2(total, e): # FIXED: refunds reduce revenue
return total - e["amount"] if e["type"] == "refund" else total + e["amount"]
# ---------- KAPPA: one streaming code path; fix a bug by replaying the log ----------
print("KAPPA")
print(f" {'v1 job (live since morning) says revenue':44}: Rs {consume(v1)}")
print(" bug found -> deploy v2, replay from offset 0")
print(f" {'v2 job after replay says revenue':44}: Rs {consume(v2)}")
# ---------- LAMBDA: batch view (complete but late) + speed view (recent, fast) ----------
print("\nLAMBDA")
batch_upto = 5 # last night's batch job processed offsets 0-4
batch_view = consume(v2, 0, batch_upto)
speed_view = consume(v2, batch_upto) # events that arrived since the batch ran
print(f" {'batch view (offsets 0-4, computed at 02:00)':44}: Rs {batch_view}")
print(f" {'speed view (offsets 5-7, live since then)':44}: Rs {speed_view}")
print(f" {'query answer = batch view + speed view':44}: Rs {batch_view + speed_view}")
# In a terminal: python replay_log.pyRecorded from a real run; your output will be identical. Both architectures give the same correct answer (₹410) from the same log. The speed view is negative because a ₹300 refund arrived after the batch run, a reminder that real-time views hold whatever happened recently, including corrections.
A serving-layer query, one line at a time
The batch view was computed overnight; the speed layer adds today's events one by one. Watch speed change with each event while batch stays fixed, and the query merge them at the end.
Worked examples: real problems, solved and traced in memory
Four problems on merging views, catching up, code drift and replay planning. Each runs live and is traced.
Combine yesterday's exact total with today's live total
Problem statement
The batch view says the season's revenue till last night is ₹18.40 crore. The speed layer has today's events: ₹1.12 crore so far, then two more updates of ₹0.80 crore and ₹0.39 crore. Print the dashboard number after each update.
batch 18.40 cr · today: 1.12, +0.80, +0.39 (cr)
dashboard: 19.52 cr dashboard: 20.32 cr dashboard: 20.71 cr
Approach
- Keep the batch view fixed.
- Add each live update to the speed view.
- Print batch + speed after every update.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Move events from the speed view to the batch view
Problem statement
Events have offsets 0–9. Last night's batch covered offsets 0–5. Tonight's batch covers 0–9. Show which offsets each layer is responsible for, before and after.
offsets 0–9, cut-off 6 then 10
cutoff 6 | batch: [0, 1, 2, 3, 4, 5] | speed: [6, 7, 8, 9] cutoff 10 | batch: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | speed: []
Approach
- The batch layer owns offsets below the cut-off.
- The speed layer owns offsets from the cut-off onwards.
- Print both lists for each cut-off.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Detect drift between batch and stream logic
Problem statement
The same day's events are processed by the batch code and by the stream code. Finance's rule says orders cancelled within 5 minutes are not sales. Find whether the two implementations disagree and by how much.
events with minutes-until-cancel (None = not cancelled)
batch: 510 | stream: 820 | drift: 310
Approach
- Implement both functions as they exist in production.
- Run both on the same events.
- Report the difference, which should be zero.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Can we replay 90 days before the morning meeting?
Problem statement
A rule change must be applied to 90 days of history. The log holds 3 million events per day. The replay job processes 80,000 events per second, and new events arrive at 40 per second. Will it catch up within 8 hours?
3,000,000/day × 90 days · replay 80,000/s · live 40/s · budget 8 h
Backlog: 270,000,000 events Catch-up time: 0.94 hours Fits in 8 hours? True
Approach
- Backlog = events per day × days.
- The replay closes the gap at (replay speed − live speed) per second.
- Compare the hours needed with the 8-hour budget.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Check yourself
Quick revision
Lesson 4.4 in one screen
- Lambda = batch layer (exact, hours old) + speed layer (fresh, approximate) + serving layer (merge).
- Query = batch view + real-time view; real-time views are discarded once batch catches up.
- Lambda drawback: two codebases for the same logic, which can drift apart.
- Kappa = append-only log + one stream job + serving store; reprocess by replaying the log.
- Kappa needs long retention, fast replay, exactly-once sinks; switch versions blue-green.
- Lambda: Nathan Marz (2011). Kappa: Jay Kreps (2014).
- Modern practice: unified engines and lakehouse tables blend both ideas. Choose per use case.
Interview and viva questions
Explain the Lambda architecture with a diagram.
Incoming data is sent to two paths. The batch layer appends it to an immutable master dataset and periodically recomputes batch views over all data (accurate but high-latency). The speed layer processes only recent data in real time to produce real-time views (low-latency, approximate). The serving layer stores both and answers queries by merging them. When the batch layer catches up, the corresponding real-time views are discarded.
What is the Kappa architecture and how is it different from Lambda?
Kappa uses a single stream-processing path over an append-only, replayable log (such as Kafka with long retention). There is no separate batch layer: to fix bugs or compute new metrics, a new version of the stream job replays the log from the beginning into a new output, and readers switch to it once it catches up. This avoids maintaining two codebases, at the cost of relying on long retention and robust streaming.
What are the advantages and disadvantages of Lambda?
Advantages: accurate results from full recomputation, low-latency views from the speed layer, and robustness, since batch recomputation corrects any errors. Disadvantages: duplicated logic in two systems, more operational complexity, and possible inconsistencies between batch and real-time results.
When would you choose Kappa over Lambda?
When the processing logic can be expressed as stream jobs, the event log can retain enough history to replay, logic changes are frequent, and the team wants one codebase. Lambda (or a batch component) remains useful when recomputation needs heavy historical joins, very long history, or complex batch-only tools such as large model training.
What is the role of the serving layer?
It stores precomputed batch views and real-time views in a database built for fast reads (for example Cassandra, HBase, Druid or a warehouse) and answers application and dashboard queries by combining them, so users get complete and up-to-date results with low latency.
Practice problems
Exercises on views, replays and planning, from easy to advanced, ending with two business decisions.
Merge two views
The batch view is 9,450 orders and the real-time view is 312 orders. Print the total the dashboard shows.
9450 + 312
Dashboard: 9762
Show solution and explanation
batch, speed = 9450, 312
print("Dashboard:", batch + speed)How it works: The serving layer's core operation: batch view + real-time view.
Which layer answers?
For each requirement, print which Lambda layer provides it.
“seconds-old count”, “exact last month”, “combined answer”
seconds-old count -> speed layer exact last month -> batch layer combined answer -> serving layer
Show solution and explanation
needs = {"seconds-old count": "speed layer", "exact last month": "batch layer",
"combined answer": "serving layer"}
for need, layer in needs.items():
print(f"{need:18} -> {layer}")How it works: Fresh = speed, exact = batch, merged = serving.
Log storage for retention
An app produces 5 lakh events a day of 500 bytes each. How many GB does a 30-day log need (one copy)?
500000 × 500 B × 30
6.98 GB
Show solution and explanation
print(round(500_000 * 500 * 30 / 1024**3, 2), "GB")How it works: Retention size = events per day × size × days. Multiply by the replication factor for real storage.
Speed view after the cut-off
Event times are given in hours. The batch ran at hour 24. Sum only the amounts the speed layer handles (time ≥ 24).
(20,100), (23,50), (24,70), (26,30)
Speed view: 100
Show solution and explanation
events = [(20, 100), (23, 50), (24, 70), (26, 30)]
cutoff = 24
print("Speed view:", sum(a for t, a in events if t >= cutoff))How it works: Everything before the cut-off belongs to the batch view; counting it twice would be a bug.
How wrong was the live number?
The live counter showed ₹2.31 crore; the batch result is ₹2.24 crore. Print the difference as a percentage of the batch result.
2.31 vs 2.24
3.1 % higher than exact
Show solution and explanation
live, exact = 2.31, 2.24
print(round((live - exact) / exact * 100, 1), "% higher than exact")How it works: Speed-layer results are useful for decisions but are not the final, audited number.
Replay with corrected logic
Replay the log with the corrected rule (refunds subtract) and compare with the buggy total (refunds add).
sale 500, refund 200, sale 300, refund 100
Buggy: 1100 | Fixed after replay: 500
Show solution and explanation
log = [(500, "sale"), (200, "refund"), (300, "sale"), (100, "refund")]
buggy = sum(a for a, k in log)
fixed = sum(-a if k == "refund" else a for a, k in log)
print("Buggy:", buggy, "| Fixed after replay:", fixed)How it works: Because the log keeps every event, the corrected logic can recompute history exactly.
When can we switch?
Job v2 starts 2 crore events behind live. It processes 40,000 events/s while new events arrive at 500/s. Print the minutes until it catches up.
backlog 20,000,000; 40,000/s vs 500/s
8.4 minutes until v2 catches up
Show solution and explanation
backlog = 20_000_000
minutes = backlog / (40_000 - 500) / 60
print(round(minutes, 1), "minutes until v2 catches up")How it works: Only when the new version has caught up do you switch readers to its output (blue-green).
The cost of two codebases
Each new metric takes 3 days to build in batch and 4 days in streaming. A unified engine needs 5 days once. For 12 metrics a year, print the engineer-days for Lambda and for unified.
12 metrics
Lambda (two codebases): 84 days Unified engine : 60 days
Show solution and explanation
metrics = 12
print("Lambda (two codebases):", metrics * (3 + 4), "days")
print("Unified engine :", metrics * 5, "days")How it works: Duplicate implementation is Lambda's hidden running cost (numbers illustrative).
Does the replay fit the window?
A full replay takes 6.5 hours and must finish between 22:00 and 06:00. Print whether it fits and how much buffer remains.
6.5 h vs 8 h window
Fits: True | buffer: 1.5 hours
Show solution and explanation
replay_h, window_h = 6.5, 8
print("Fits:", replay_h <= window_h, "| buffer:", window_h - replay_h, "hours")How it works: Replay time is a real constraint in Kappa designs; plan capacity so replays finish in time.
Real-world scenario questions (asked in company interviews)
Architecture questions asked in data engineering interviews. Decide your answer first, then open the model answer.
“Why do the live and final numbers differ?”
The business head sees ₹2.31 crore on the live sale dashboard and ₹2.24 crore in the next day's report and thinks the system is broken.
The interviewer is checking: can you explain speed vs batch results to a non-technical person?
Show a strong answer
The live number comes from the speed layer: it is computed within seconds, so it can't yet account for late refunds, duplicate events from retries, or orders that arrive late. The next-day number comes from the batch layer, which recomputes from all raw events once everything has arrived. Live is for decisions during the sale; batch is the audited figure.
- Label the live figure as “provisional” on the dashboard.
- Show yesterday's and older figures from the batch view only.
Avoid: saying one of them is “wrong”. Each answers a different question.
Real-time budget cap and monthly invoices
An ad platform must stop a campaign within seconds of hitting its daily budget, and send advertisers an exact monthly invoice after removing fraudulent clicks. Propose an architecture.
The interviewer is checking: can you map requirements to speed and batch paths?
Show a strong answer
- Speed path: clicks → Kafka → stream job keeping per-campaign spend for today → stops campaigns at the cap.
- Batch path: all clicks stored in the lake → nightly Spark job applies fraud filtering and de-duplication → exact spend per campaign → invoices.
- Serving: advertiser dashboard shows exact spend for past days and provisional spend for today.
- Keep the spend logic in one shared library, or use a unified engine, to limit drift.
Avoid: invoicing from the real-time counters.
A bug corrupted three weeks of metrics
A Kappa-style platform's stream job had a currency-conversion bug for three weeks. Kafka retention is 14 days. How do you fix the history?
The interviewer is checking: do you understand Kappa's dependence on retention, and the backups?
Show a strong answer
- Fix the job and start v2 from the earliest offset still in Kafka (14 days) into a new table.
- For the 7 days no longer in Kafka, reprocess from the copy of raw events archived in the lake (most teams keep one), using the same v2 code in batch mode if the engine allows.
- Validate v2 against known totals, then switch readers (blue-green) and stop v1.
- Afterwards: increase retention or enable tiered storage, and keep raw events in the lake as a safety net.
Avoid: patching old results by hand with an estimated correction factor.
“Should we migrate our Lambda platform to Kappa?”
A company runs Lambda: Spark nightly jobs and Flink streaming jobs with duplicated logic. Numbers drift every month. Leadership asks whether to move to Kappa.
The interviewer is checking: balanced reasoning about trade-offs, not a slogan.
Show a strong answer
- Classify metrics: simple aggregations (counts, revenue) are good Kappa candidates; heavy historical analytics and ML training may stay batch.
- Check prerequisites: log retention and cost, replay speed, exactly-once sinks, team streaming skills, schema evolution over long histories.
- Middle path: move duplicated logic into a unified engine (Flink or Spark Structured Streaming) so the same code runs live and over historical tables in the lakehouse.
- Migrate metric by metric, running old and new in parallel and comparing outputs before switching.
Avoid: a big-bang rewrite, or claiming Kappa is always better.