Final over: the live score and tomorrow's newspaper
It's the T20 final. Riya is watching the live score on her phone: every ball updates within a second. Kabir's father prefers the newspaper: tomorrow morning he will read the complete scorecard, every batter's strike rate and a chart of where the runs came from. Same match, same data, two completely different ways of processing it.
Sir, my app showed the six before the TV commentator finished shouting!
Because your app is fed by a stream: each ball becomes an event, and the system updates the score the moment it arrives. The newspaper uses batch processing: wait for the whole match, then compute everything at once.
At TiffinGo our sales report is batch, at 2 am. But the kitchen-overload alert was a stream. How do we decide which one to use?
Ask one question: how old can the answer be before it becomes useless? A fraud alert one day late is useless. A monthly salary report computed in one second is pointless.
So stream is better because it's faster?
Faster, but harder and costlier, Riya. Today you'll see why, by processing the same data both ways, watching a stolen phone get stopped in real time, and learning what happens when an event arrives late from a train in a tunnel.
What are batch and stream processing?
Batch processing processes a bounded, finite set of data that has been collected over a period, usually on a schedule, producing results with high throughput but high latency (minutes to hours). Stream processing processes unbounded data continuously, event by event (or in very small micro-batches) as it arrives, producing results with low latency (milliseconds to seconds). Stream processors typically group events into windows and keep state between events.
Batch: collect everything first, then process it in one go, like doing a week's laundry on Sunday. Stream: process each item the moment it arrives, like washing each cup as soon as you finish your tea.
Words you'll meet in this lesson
Batch and stream at a glance
| Batch | Bounded data · scheduled (hourly, nightly) · latency minutes–hours · simpler, cheaper, easy to re-run · Spark, Hive, MapReduce, dbt |
|---|---|
| Stream | Unbounded data · always running · latency milliseconds–seconds · needs state, windows, late-data handling · Flink, Kafka Streams, Spark Structured Streaming |
Batch vs stream at three levels
Beginner Bucket vs tap
Batch is filling a bucket and carrying it once: efficient, but you wait. Stream is drinking straight from the tap: instant, but the tap must always be running.
- Batch examples: salary credited on the 1st, electricity bill each month, college result after all papers are checked, the morning sales report.
- Stream examples: live cricket score, UPI fraud check before money leaves, OTP delivery, a cab's live location on the map.
The one question: how old can the answer be before it is useless? Seconds → stream. Hours or days → batch.
Intermediate Latency, throughput and windows
- Trade-off: batch maximises throughput and simplicity; stream minimises latency but runs 24×7 and is harder to build and test.
- Windows turn an endless stream into finite groups: tumbling (fixed, non-overlapping: runs per over), sliding (overlapping: orders in the last 5 minutes, updated every minute) and session (a user's activity until a 30-minute gap).
- Event time vs processing time: a payment made at 10:00:00 may reach the system at 10:00:05, or later. Correct results group by event time.
- Micro-batch vs true streaming: Spark Structured Streaming processes small batches every few seconds (or less); Flink processes each event as it arrives. Both are “streaming” for most business needs.
- Tools: batch with Spark/Hive/dbt scheduled by Airflow; streams with Kafka plus Flink, Kafka Streams or Spark Structured Streaming.
Advanced Late data, guarantees and state
- Out-of-order and late events are normal (phones offline, network retries). A watermark tells the processor “I believe all events up to time T have arrived”; windows close at the watermark, and an allowed lateness policy decides what happens to events that come after: update the result, send to a side output, or drop.
- Delivery guarantees: at-most-once (may lose events), at-least-once (may duplicate), exactly-once (each event affects the result once), achieved with checkpointed state plus idempotent or transactional sinks.
- State and checkpoints: running totals and per-card histories live in a state store that is periodically checkpointed, so a crashed job resumes without losing or double-counting.
- Backpressure: when a downstream step is slow, the stream must slow the input instead of running out of memory.
- Reprocessing: fixing a bug in a stream job means replaying old events from Kafka or the lake. This leads directly to the Lambda and Kappa architectures (Lesson 4.4).
Exam tip: Compare batch and stream in a table on data (bounded/unbounded), latency, throughput, processing trigger, complexity, examples and tools. Add the three window types with one example each. That covers most 5- and 10-mark questions.
Three processing styles, three window types
| Batch | Micro-batch | Stream (event at a time) | |
|---|---|---|---|
| Input | A day's or hour's files | Events collected for a few seconds | Each event as it arrives |
| Latency | Minutes to hours | Around a second to several seconds | Milliseconds |
| Typical tools | Spark, Hive, dbt, MapReduce | Spark Structured Streaming | Flink, Kafka Streams |
| Good for | Reports, billing, model training, backfills | Near-real-time dashboards, ETL from Kafka | Fraud checks, alerts, live tracking |
| Running cost | Only while the job runs | Always on | Always on |
| Fixing mistakes | Re-run the job | Replay from Kafka | Replay from Kafka; manage state carefully |
Window types
Tumbling window
Fixed size, no overlap, every event in exactly one window. Runs per over, orders per minute, sales per day.
Fixed bucketsSliding window
Fixed size, moves forward in small steps, so windows overlap. Payments in the last 2 minutes, checked every 10 seconds.
Moving viewSession window
Grows while events keep coming and closes after a gap of inactivity. One shopping visit ends after 30 minutes idle.
Activity-basedDelivery guarantees
| Guarantee | What can go wrong | Acceptable for |
|---|---|---|
| At-most-once | Some events are lost | Approximate metrics, e.g. page-view counters |
| At-least-once | Some events are counted twice | Most pipelines, if results are de-duplicated |
| Exactly-once | Nothing, but it costs more effort and some speed | Payments, billing, balances |
Don't stream what nobody reads in real time. A daily report built on a 24×7 stream costs more and breaks more often than a nightly batch job. Choose stream only when a late answer loses money, safety or customers.
A UPI payment's fraud check, in under a second
The payment event arrives
The bank's app publishes the event to a Kafka topic before the money moves. The stream processor reads it within milliseconds.{"txn": "T88214", "payer": "aman@okbank", "amount": 4900, "device": "new-android-7f", "city": "Kolkata", "event_time": "10:02:11"}Look up state for this payer
The processor keeps state per payer: usual city, known devices and the last few minutes of payments. This lives in the processor's state store, not in a slow database call.usual_city = "Pune"; known_devices = {"iphone-12a"} recent_payments = [("10:00:40", 4900), ("10:01:05", 4900), ("10:01:31", 4900), ("10:01:50", 4900)]Apply a sliding-window rule
Five payments of ₹4,900 within two minutes, from a new phone, in a city 1,600 km from usual. A sliding window of 2 minutes catches it.device, city = "new-android-7f", "Kolkata" # from the new event in_last_2_min = [p for p in recent_payments if p[0] >= "10:00:11"] suspicious = len(in_last_2_min) >= 4 and device not in known_devices and city != usual_city print(suspicious)TrueDecide and respond
The payment is put on hold and the customer gets a call: “Did you make these payments?”
The whole check took about 80 milliseconds. A batch fraud report tomorrow morning would have found the same pattern, after the money was gone.Batch still has a job
The same events are also written to the lake.
Every night a batch job uses months of history to retrain the fraud model and tune the rules. Stream makes the decision; batch makes the decision-maker smarter. Most real systems use both.
The same overs, processed as a stream and as a batch
Step through the six frames. Left: Riya's phone, then the newspaper. Right: the stream processor during the match, and the batch job after it.
- First ball: a six. Riya's app shows 6/0 within half a second.
- Over 2, ball 2: wicket. The score becomes 10/1 instantly.
- End of over 2: the app shows 10 runs this over.
- A slow network delays one ball's update; the score jumps when it finally arrives.
- Next morning, the newspaper prints a full scorecard, run rate and a wagon wheel.
- Same events, two kinds of answer: fast during the match, complete and deep after it.
- The ball event enters Kafka. The stream processor updates its state (total = 6) and pushes the new score to phones in under half a second.
- For the wicket, only the wickets counter changes. A stream processor keeps a few numbers of state, not the whole match.
- After the 6th ball, the tumbling window “over 2” closes and its result (10 runs) is emitted.
- One update arrives late and out of order. Grouping by event time puts it in the right over; a watermark decides how long to wait for stragglers.
- After the match the data is bounded: a Spark batch job at 1 am processes all 240 balls together and computes every statistic, joining other tables.
- Stream: low latency, a few running numbers. Batch: high latency, complete and detailed. Both are needed.
Real-time scenarios where the choice matters
Three live scenarios: one where only streaming saves the day, one that shows the three window types on a real timeline, and an advanced one about events that arrive late.
Real-time scenario 1A stolen phone: stream stops it, batch reports it
Aman's phone is stolen in a crowded market. The thief starts sending UPI payments of ₹4,900 every 20–30 seconds. Watch the stream processor's 2-minute sliding window, and compare with a nightly batch check.
- 10:00:40: the first payment from the stolen phone. One unusual payment alone isn't enough to block, so it goes through.
- 10:01:50: four payments inside the 2-minute sliding window. The risk score climbs.
- 10:02:11: the fifth payment crosses the rule. The payment is held and the account frozen, about 80 ms after the event arrived.
- The next attempt is blocked. The loss stops at four payments (₹19,600) instead of the whole balance.
- A batch fraud report the next night would have spotted the same pattern, but far too late to stop it. (Rules and amounts are illustrative.)
One timeline, three windows
The same food orders from one office building between 13:00 and 13:40. Watch how tumbling, sliding and session windows group them differently, and why each answers a different business question.
- The events: ten orders from one building between 13:00 and 13:40, bunched at lunch with a gap afterwards.
- Tumbling: fixed 10-minute buckets that never overlap. Each order is counted once: 4, 4, 0 and 2.
- Sliding: a 20-minute window that moves forward every 5 minutes. Windows overlap, so the answer is always “recent”.
- Session: a window that keeps growing while orders keep coming and closes after 10 minutes of silence: one long rush, then a separate pair.
- Pick the window from the question you need answered.
The order from the tunnel: late data and watermarks
A passenger on a local train orders food at 18:58, just as the train enters a tunnel. The phone stays offline for 4 minutes and the event reaches the system at 19:02. Which minute should it count in, and how long should the system wait?
- 18:58: the passenger orders in the tunnel. The phone can't send the event yet.
- 19:02: the event arrives. Its event time is 18:58 but its processing time is 19:02. Grouping by processing time would put it in the wrong window.
- The stream processor uses a watermark: the latest event time seen minus an allowed delay (here 3 minutes). The 18:55–19:00 window stays open until the watermark passes 19:00.
- The late event arrived before the watermark, so it is counted in the correct window: 4 orders, not 3. The price is a result published a few minutes later.
- Every streaming system chooses a balance between speed and completeness, and decides what to do with events that arrive after the watermark.
Which one do real companies use?
1. Stock market: stream
Price ticks arrive many times per second. Trading platforms update charts, trigger stop-loss orders and run risk checks on the stream. End-of-day settlement and tax reports are batch.
2. Payroll: batch
Salaries are computed once a month from attendance, leave and tax data. Nobody needs them every second; correctness and an audit trail matter far more. A scheduled batch job is the right tool.
3. Factory sensors: stream
Machines report temperature and vibration every second. A stream job raises an alert when readings stay above a limit for 30 seconds, before a breakdown. Weekly maintenance planning runs as batch on the same data.
4. Live streaming apps: both
The “watching now” counter and buffering alerts come from a stream of player heartbeats. Royalty payments and next-day “top shows” lists come from batch jobs over the complete data.
5. Online ads: stream for spend, batch for bills
Click events are streamed so a campaign stops spending the moment its daily budget is used. Advertisers' monthly invoices are computed in batch after de-duplicating and removing fraudulent clicks.
Examples are simplified; real systems combine both styles. Rules and numbers shown are illustrative.
The same orders, processed two ways
Six orders arrive during the day. The stream job updates its answer with every event; the batch job waits for the complete list and answers once. Compare when each answer is available. Then try windows, a fraud rule, late events and duplicates in the challenges.
orders = [ # (minute, kitchen, amount)
(1, "K12", 120), (1, "K11", 90), (2, "K12", 250),
(3, "K12", 180), (3, "K13", 60), (4, "K12", 140),
]
# STREAM: update the answer as each event arrives
def stream_job(events):
total = 0
for minute, kitchen, amount in events:
total = total + amount
print(f"[stream @ minute {minute}] +{amount:>3} from {kitchen} -> running total Rs {total}")
# BATCH: wait until all data is collected, then process it in one go
def batch_job(all_orders):
total = sum(amount for _, _, amount in all_orders)
busiest = max(set(k for _, k, _ in all_orders),
key=lambda k: sum(a for _, kk, a in all_orders if kk == k))
print(f"[batch @ end of day] {len(all_orders)} orders, Rs {total}, busiest kitchen {busiest}")
stream_job(orders)
batch_job(orders)for minute, kitchen, amount in events:The stream job never sees “all” the data. It handles one event, updates its state (total) and moves on.print(f"[stream @ minute {minute}] ...An answer is available after every event: low latency.total = sum(amount for _, _, amount in all_orders)The batch job has the complete, bounded data, so it can answer harder questions in one pass, like “which kitchen was busiest overall?”
Run it on your PC: a live score stream in your terminal
This program generates a “live” ball every 0.3 seconds and processes it as a stream: the score updates immediately, and a 6-ball tumbling window closes at the end of every over. After the last ball it prints a batch summary computed from the complete data. Save it as live_score.py and watch it scroll.
Watch a stream being processed
import random, time
random.seed(27)
OUTCOMES = [0, 0, 1, 1, 1, 2, 4, 6, "W"]
def ball_stream(overs=3):
"""An endless-looking stream of balls, one every 0.3 s (sped up)."""
for over in range(1, overs + 1):
for ball in range(1, 7):
time.sleep(0.3)
yield over, ball, random.choice(OUTCOMES)
# STREAM processing: update the answer the moment each ball arrives
total, wickets, over_runs, per_over = 0, 0, 0, []
start = time.time()
for over, ball, result in ball_stream():
if result == "W":
wickets += 1
else:
total += result
over_runs += result
print(f"{time.time() - start:4.1f}s ball {over - 1}.{ball} {str(result):>2} score {total}/{wickets}")
if ball == 6: # the "over" window closes
print(f" --- over {over}: {over_runs} runs ---")
per_over.append(over_runs)
over_runs = 0
# BATCH processing: after the match, compute from the complete data
print("Batch summary after the match:", per_over, "| run rate", round(total / len(per_over), 2))
# In a terminal: python live_score.pyRecorded from a real run. The balls are the same every time (the random generator is seeded); the timings in the first column will differ slightly on your computer. Notice that each line appears 0.3 s after the previous one, as a real stream would, while the batch summary appears only at the very end.
A tumbling window, one event at a time
Orders arrive with the minute they happened. The stream job keeps a running total for the current minute; when an event from a new minute arrives, the old window closes and its result is printed. Watch window and total in the frame.
Worked examples: real problems, solved and traced in memory
Four problems from first-week analyst to streaming engineer. Each runs live and is traced line by line.
Batch or stream? Decide from the deadline
Problem statement
For each use case, the business says how old the answer may be (in seconds) before it becomes useless. Classify each: under 60 seconds → stream; otherwise → batch.
fraud check 1 s · live score 2 s · salary 86400 s · monthly bill 604800 s · surge price 30 s · sales report 21600 s
fraud check STREAM live score STREAM salary batch monthly bill batch surge price STREAM sales report batch
Approach
- Store each use case with its maximum acceptable age.
- Loop over them and compare with 60 seconds.
- Print the choice.
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.
Runs per over from ball-by-ball events
Problem statement
A sports app receives one event per ball. Produce the runs in each over (a 6-ball tumbling window) and the over with the most runs.
12 balls: 6,1,1,1,0,0, 1,W,2,1,2,4
Runs per over: [9, 10] Best over: 2
Approach
- Walk through the balls, adding runs (a wicket adds 0).
- Every 6th ball, the window closes: save the over's runs and reset.
- Find the best over with
max.
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.
Flag a card used too often
Problem statement
For each card payment, count how many payments the same card made in the last 60 seconds. Flag any payment where the count exceeds 3.
times (s): 0, 10, 20, 30, 45, 200
0 1 ok 10 2 ok 20 3 ok 30 4 FLAG 45 5 FLAG 200 1 ok
Approach
- Keep a list of recent payment times for the card.
- For each new payment, drop times older than 60 s, then add the new one.
- Flag if the list is longer than 3.
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.
Count orders per 10-minute window with a watermark
Problem statement
Order events arrive out of order. Count orders per 10-minute window by event time, using a watermark of “largest event time seen minus 5 minutes”. Events behind the watermark go to a late list.
event times (minute): 1, 4, 12, 8, 15, 3, 22
Windows: {0: 3, 10: 2, 20: 1}
Late: [3]Approach
- Track the largest event time seen; the watermark is that minus 5.
- If an event is older than the watermark, it is late: keep it aside.
- Otherwise add it to its window:
(t // 10) * 10.
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.2 in one screen
- Batch: bounded data, scheduled, high latency, high throughput, simpler. Stream: unbounded, continuous, low latency, needs state.
- Decide by: how old can the answer be before it is useless?
- Windows: tumbling (fixed, no overlap) · sliding (overlapping, recent) · session (ends after a gap).
- Event time = when it happened; processing time = when it was handled. Group by event time.
- Watermark = how long to wait for late events; a trade-off between speed and completeness.
- Guarantees: at-most-once (loss), at-least-once (duplicates), exactly-once (neither).
- Tools: batch → Spark, Hive, dbt + Airflow · stream → Kafka + Flink, Kafka Streams, Spark Structured Streaming.
Interview and viva questions
Differentiate batch processing and stream processing.
Batch processes a bounded dataset collected over time, on a schedule, with high throughput and latency of minutes to hours (daily reports, billing, model training). Stream processing handles unbounded data continuously as events arrive, with latency of milliseconds to seconds, keeping state and using windows (fraud detection, live dashboards, alerts). Batch is simpler and cheaper; stream is faster but harder to build and operate.
What are windows in stream processing? Explain their types.
Windows divide an endless stream into finite groups for calculation. Tumbling windows are fixed-size and non-overlapping (orders per minute). Sliding windows are fixed-size but overlap, moving forward in small steps (payments in the last 5 minutes, every 30 seconds). Session windows grow with activity and close after a gap of inactivity (a user's visit).
What is the difference between event time and processing time?
Event time is when an event actually occurred, recorded at the source. Processing time is when the system processes it. Network delays and offline devices make them differ, so correct aggregations group by event time and use watermarks to handle late events.
What is a watermark?
A watermark is the stream processor's estimate that all events up to a certain event time have arrived, usually the maximum event time seen minus an allowed delay. When the watermark passes the end of a window, the window is finalised; events arriving later are handled by an allowed-lateness policy.
What is micro-batching?
Processing a stream as a series of very small batches, every few seconds or less, as Spark Structured Streaming does by default. It reuses batch machinery and gives good throughput with near-real-time latency; event-at-a-time engines like Flink can reach lower latency.
Practice problems
Window and latency exercises, from easy to advanced. The last two are business decisions about cost and service levels.
Classify by latency
Print “stream” if the required latency is under 10 seconds, else “batch”, for 2, 3600 and 0.5 seconds.
[2, 3600, 0.5]
2 -> stream 3600 -> batch 0.5 -> stream
Show solution and explanation
for need in [2, 3600, 0.5]:
print(need, "->", "stream" if need < 10 else "batch")How it works: The required latency decides the style, not the size of the data.
Throughput
A stream processed 9,00,000 events in 5 minutes. Print events per second.
900000 events, 300 s
3000.0 events per second
Show solution and explanation
print(900000 / 300, "events per second")How it works: Throughput = events ÷ seconds.
Hourly batch size
An app produces 2,500 events per second. How many events does one hourly batch contain?
2500 events/s
9,000,000 events per hourly batch
Show solution and explanation
print(f"{2500 * 3600:,} events per hourly batch")How it works: Batch size grows with the interval; that is why batch jobs need many machines.
10-minute tumbling windows
Given order minutes, count orders in each 10-minute window (0, 10, 20 …).
[1, 3, 9, 12, 15, 27, 28, 29]
{0: 3, 10: 2, 20: 3}Show solution and explanation
minutes = [1, 3, 9, 12, 15, 27, 28, 29]
counts = {}
for m in minutes:
w = (m // 10) * 10
counts[w] = counts.get(w, 0) + 1
print(counts)How it works: (m // 10) * 10 maps each minute to the start of its window.
Moving average of 3 readings
For each temperature reading, print the average of the last 3 readings (a sliding window of size 3).
[30, 32, 34, 40, 41]
30 -> avg of last 3: 30.0 32 -> avg of last 3: 31.0 34 -> avg of last 3: 32.0 40 -> avg of last 3: 35.3 41 -> avg of last 3: 38.3
Show solution and explanation
temps = [30, 32, 34, 40, 41]
for i in range(len(temps)):
window = temps[max(0, i - 2):i + 1]
print(temps[i], "-> avg of last 3:", round(sum(window) / len(window), 1))How it works: Each window overlaps the previous one: a sliding window.
Count sessions
Click times are given in minutes. A session ends after a gap of more than 20 minutes. Print the number of sessions.
[0, 5, 8, 40, 42, 90]
Sessions: 3
Show solution and explanation
clicks = [0, 5, 8, 40, 42, 90]
sessions = 1
for prev, cur in zip(clicks, clicks[1:]):
if cur - prev > 20:
sessions = sessions + 1
print("Sessions:", sessions)How it works: Each gap larger than the limit starts a new session window.
At-least-once to exactly-once
The same event ids may be delivered more than once. Print the number of unique events and how many duplicates were dropped.
[e1, e2, e2, e3, e1, e4]
Unique: 4 | duplicates dropped: 2
Show solution and explanation
events = ["e1", "e2", "e2", "e3", "e1", "e4"]
unique = []
for e in events:
if e not in unique:
unique.append(e)
print("Unique:", len(unique), "| duplicates dropped:", len(events) - len(unique))How it works: De-duplicating by a unique id turns at-least-once delivery into exactly-once results.
Always-on stream vs nightly batch
A stream job needs 4 nodes running 24 hours a day; a nightly batch needs 20 nodes for 2 hours. A node costs Rs 30 per hour. Print the monthly (30-day) cost of each (illustrative).
Rs 30/node-hour
Stream: Rs 86400 Batch : Rs 36000
Show solution and explanation
stream = 4 * 24 * 30 * 30
batch = 20 * 2 * 30 * 30
print("Stream: Rs", stream)
print("Batch : Rs", batch)How it works: Streams cost money every hour. Pay for real time only where it creates real value.
Did we meet the 1-second promise?
The bank promises a fraud decision within 1,000 ms. Given decision times, print the percentage that met the promise.
[80, 95, 120, 1500, 70, 90, 2100, 85]
75 % within 1 second
Show solution and explanation
times_ms = [80, 95, 120, 1500, 70, 90, 2100, 85]
ok = sum(1 for t in times_ms if t <= 1000)
print(round(ok / len(times_ms) * 100), "% within 1 second")How it works: Streaming systems are judged by latency targets (SLAs), usually measured as percentages like this.
Real-world scenario questions (asked in company interviews)
Situations where choosing batch or stream, or handling time correctly, is the real test. Decide your answer first, then open the model answer.
“Make the daily sales report real-time”
A store manager asks for the daily sales report, which is read once each morning, to be built on Kafka and Flink “because real-time is modern”.
The interviewer is checking: do you choose based on the business need, not on fashion?
Show a strong answer
Ask how the report is used. If it is read once at 9 am, an answer at 6 am is as good as one at 8:59:59. A nightly batch job is cheaper, simpler to test and easy to re-run.
- If the manager actually needs intraday decisions (restocking during a sale), offer an hourly batch or a small live counter for just that metric.
- Keep stream processing for cases where a late answer loses money or customers.
Avoid: building a 24×7 streaming system for a report nobody reads in real time.
Fraud is found the next morning
A wallet company's fraud report runs every night. Losses keep growing because stolen accounts are emptied within minutes. Design the change.
The interviewer is checking: can you design a basic streaming flow with state, windows and a fallback to batch?
Show a strong answer
- Publish every payment attempt to Kafka before it is completed.
- A stream processor (Flink or Spark Structured Streaming) keeps per-account state: recent payments, known devices, usual location.
- Apply rules and a model score over sliding windows (for example, payments in the last 2 minutes) and return allow, hold or block within the latency budget.
- Keep the nightly batch job to retrain the model and tune rules on full history.
- Monitor latency and false positives; blocking genuine customers has a cost too.
Avoid: running the nightly job every 5 minutes and calling it real time; it still can't stop a payment in progress.
The live revenue counter is higher than the bank's figure
A streaming dashboard shows ₹1.02 crore for today; the settlement file says ₹1.00 crore. The stream job restarted twice today after crashes.
The interviewer is checking: do you understand delivery guarantees and how restarts cause duplicates?
Show a strong answer
The pipeline is probably at-least-once: after each crash it re-read events since its last checkpoint and counted some twice.
- Make results idempotent: de-duplicate on transaction id, or write with upserts keyed by id.
- Use the engine's checkpointing with transactional or idempotent sinks for exactly-once results.
- Reconcile daily against the settlement file and alert on differences.
Avoid: “fixing” the dashboard by subtracting an estimated difference.
Players' events arrive hours late
A mobile game sends events when the phone is online. Players on flights or in villages with weak signal send events hours later. Hourly “active players” counts keep changing after they are published.
The interviewer is checking: event time, watermarks, allowed lateness and how batch corrects stream results.
Show a strong answer
- Aggregate by event time (from the phone), not by arrival time.
- Set a watermark that matches business needs, e.g. 10 minutes, and publish hourly counts as provisional.
- Send events later than the allowed lateness to a side output instead of dropping them.
- Run a nightly batch job over the complete data in the lake to publish final numbers. That is the Lambda idea from Lesson 4.4.
- Guard against wrong device clocks: reject or clamp event times far in the future.
Avoid: waiting hours before showing any number, or silently dropping late events.