Batch vs Streaming: Intermediate
What you will be able to do
Latency vs Throughput Tradeoff
Distinguish latency and throughput as separate metrics and identify which one constrains a given pipeline.
Latency and Throughput Defined
| Metric | What It Measures | Unit |
|---|---|---|
| End-to-end latency | Time from event arrival at the source to event visible at the consumer | Milliseconds, seconds, minutes |
| Processing latency | Time the pipeline spends actively transforming the event | Milliseconds, microseconds |
| Throughput | Events processed per unit time | Events per second, GB per hour |
| Cost per event | Compute and storage cost amortized over each event | Dollars per million events |
Concrete Numbers from Real Workloads
| Workload | Throughput | Latency |
|---|---|---|
| Nightly Spark batch on 100GB | Roughly 5 million events processed per minute during the run | Up to 24 hours from arrival to result |
| Hourly batch on 4GB | Roughly 700K events per minute during the run | Up to 1 hour |
| Spark Structured Streaming, 1-min trigger | Roughly 80K events per second sustained | 60 to 90 seconds end to end |
| Flink, true streaming on Kafka | Roughly 100K events per second per task slot | 100ms to a few seconds |
| Custom low-latency C++ trading system | Lower aggregate throughput, dedicated path | Sub-millisecond |
Why Batch Wins on Throughput
Why Streaming Wins on Latency
- Throughput per dollar of compute
- Amortized per-event overhead
- Predictable resource usage during the run window
- Simple failure recovery: rerun the chunk
- Latency from arrival to processed
- Steady, predictable end-to-end time
- Continuous resource availability
- Continuous progress instead of stop-and-go cycles
The Tradeoff in One Number
- ▸What is the latency target in concrete units (milliseconds, seconds, minutes)?
- ▸What is the throughput target in events per second at peak?
- ▸What is the budget per million events processed?
- ▸Which dimension is firm and which is negotiable?
Two ways data moves: batch processes a whole chunk on a schedule (accurate, delayed); streaming processes each event as it arrives (fast, continuous). The latency SLA decides which.
Micro-Batch: The Middle Ground
Apply the micro-batch pattern by setting an explicit trigger interval and explain why it sits between pure batch and pure streaming.
How Micro-Batch Works
| Aspect | Pure Streaming | Micro-Batch |
|---|---|---|
| Trigger | Every event arrival | A configured interval (10 sec, 1 min, 5 min) |
| Per-event work | Paid per event | Paid per micro-batch; amortized within the batch |
| End-to-end latency | Bounded by single-event processing time (100ms to a few seconds) | Bounded by the trigger interval (10 sec to 5 min) |
| Engine examples | Flink, Kafka Streams, custom | Spark Structured Streaming, Flink in batch mode, dbt incremental |
| State handling | Continuous in-memory state | State checkpointed each batch; smaller surface for bugs |
The Spark Structured Streaming Pattern
Why Micro-Batch Exists
When Micro-Batch Is the Right Answer
- ▸The freshness target is in the 1-to-15-minute range
- ▸Volume is high (more than a few thousand events per second sustained)
- ▸The team has Spark or Flink experience and wants to reuse it
- ▸State is bounded and checkpointable; not millions of independent keys
When Micro-Batch Is the Wrong Answer
| Situation | Why Micro-Batch Fails | Better Choice |
|---|---|---|
| Sub-second latency target | Trigger interval cannot go below the per-batch overhead | Pure streaming with Flink, Kafka Streams, or custom |
| Very low volume | Per-batch overhead dominates; cost is wasteful | Hourly batch is cheaper and simpler |
| Per-event side effects | Batches can fail and reprocess; side effects must be idempotent | Pure streaming with at-least-once semantics and idempotent sinks |
| Unbounded state | Each batch must checkpoint state; growth is not sustainable | True streaming with key-managed state and TTLs |
Tuning the Trigger Interval
Spark Structured Streaming defaults to a 'micro-batch ASAP' mode if no trigger is set. The default is rarely right; it produces tiny batches with high per-batch overhead. Always set processingTime explicitly.
- Use micro-batch as the default for tier 2 freshness (under 15 minutes)
- Set the trigger interval to the largest value the consumer accepts
- Treat checkpointed state as recoverable; design transforms to survive a restart
- Default to a sub-second trigger because lower sounds better; cost compounds fast
- Use micro-batch when the freshness target is sub-second; the architecture cannot reach it
- Mix per-event side effects with micro-batch without idempotent sinks; replays will duplicate
Why Streaming Costs More
Estimate the cost premium of a streaming pipeline over a batch equivalent and decide when the latency justifies the spend.
Component 1: Continuous Compute
| Workload | Compute Hours per Day | Approximate Daily Cost |
|---|---|---|
| Nightly Spark batch, 4-node, 40 min run | About 2.7 compute hours | $30 to $60 per day at AWS on-demand |
| Hourly Spark batch, 4-node, 5 min runs | About 8 compute hours | $80 to $200 per day |
| Spark Structured Streaming, 1-min trigger, 4-node | 96 compute hours per day | $800 to $2,000 per day |
| Flink streaming, 4-node, always-on | 96 compute hours per day plus state nodes | $1,200 to $3,500 per day |
Component 2: State Storage
Component 3: Operational Overhead
- Failures rerun the partition; no in-flight events to worry about
- Schema changes deploy with the next run; old runs are unaffected
- Lag does not exist as a concept; freshness is bounded by schedule
- On-call sees binary signals: ran or failed
- Failures must handle in-flight events; replay logic is required
- Schema changes require a careful drain-and-redeploy
- Lag is the dominant signal; growing lag means the consumer is falling behind
- On-call sees graded signals: latency, throughput, lag percentile
When the Cost Is Worth Paying
- ▸What does each minute of latency cost the business?
- ▸Above what latency threshold does the cost become non-zero?
- ▸What is the streaming pipeline's marginal cost over a batch alternative?
- ▸Does the cost of the latency, integrated over a year, exceed the streaming bill?
The Cost Profile in One Number
- Estimate streaming costs at design time; the surprise on the bill is avoidable
- Write the dollar value of the latency the streaming provides; if zero, build batch
- Right-size streaming clusters for actual traffic, not peak-of-peak
- Compare streaming to batch on raw compute hours alone; state and operations are real
- Build streaming because batch was slow once at peak; profile first
- Default to streaming for new pipelines; the cost premium compounds across many pipelines
Stateful vs Stateless Transforms
Classify each transform as stateful or stateless and explain how the category changes cost and recovery behavior in streaming.
Stateless Transforms
| Transform | What It Does | Why It Is Stateless |
|---|---|---|
| Filter (where clause) | Drops events that fail a predicate | Decision uses only the current event |
| Projection (select columns) | Reshapes the event without combining with others | Output is a function of the single input |
| Per-event enrichment from a static lookup | Joins with a small reference table loaded into memory | Reference data is constant; not derived from event history |
| Type conversion | Casts string to int, parses JSON | Single-event operation; no history needed |
| Field-level redaction | Hashes or removes PII fields | Per-event transformation |
Stateful Transforms
| Transform | What It Does | Why It Is Stateful |
|---|---|---|
| GROUP BY aggregations | Counts, sums, averages over a key | Result depends on every event for that key seen so far |
| Windowed aggregation | Rolling counts over a time window | Result depends on which events fall inside the window |
| Stream-stream join | Matches events from two streams within a time window | Requires holding both streams in state until matched |
| Deduplication | Drops repeated events by key | Requires remembering keys already seen |
| Sessionization | Groups events into sessions by gap timeout | Each session is open until a gap closes it |
How Streaming Engines Handle State
The Cost Difference Per Category
- Memory and disk usage scale with batch size, not history
- Recovery: replay last batch or two; state is reconstructed from data
- Schema changes: relatively safe; no embedded state to migrate
- Cost premium over batch: roughly 2x to 5x
- Memory and disk usage scale with key cardinality and window size
- Recovery: restore from checkpoint; in-flight state must be consistent
- Schema changes: state migrations are required; not always trivial
- Cost premium over batch: roughly 5x to 50x
Stateful Transforms in Batch
- ▸What is the key cardinality, and how fast does it grow?
- ▸What is the window size, and how long is state kept after the window closes?
- ▸What is the watermark strategy, and how late can events arrive?
- ▸What happens to state on a failure-and-restart?
When Batch Outgrows Itself
Diagnose a slow batch pipeline and pick the smallest change that meets the consumer's freshness tier without overbuilding.
The Starting Pipeline
The Symptoms
| Symptom | Numbers | When It Started |
|---|---|---|
| Pipeline runtime stretching | From 3 hours to 11 hours | Began drifting after a 4x volume increase |
| Missed 6am SLA | Dashboard now fresh at noon, not 6am | After volume hit 18M orders/day |
| Operational pages | On-call paged 3-4 mornings a week | When run started bumping into 9am compute window |
| Marketing team built shadow pipeline | Hourly streaming consumer feeding their own dashboard | After two months of missed SLA |
The Wrong Instinct
The Right Diagnosis
| Issue | What Is Happening | Right Fix |
|---|---|---|
| Volume outgrew the cadence | 11 hours of batch cannot fit in a 7-hour overnight window | Run more often (hourly or micro-batch), each run does less work |
| Non-incremental transform | The transform reads a full day of orders even when only the last hour matters | Make the transform incremental on order_timestamp |
| Marketing wants tier-2 freshness | Batch tier-4 cannot meet a tier-2 need | Add a tier-2 path for the marketing dashboard only, leave the executive on tier 4 |
| Single pipeline serving multiple consumers | Different consumers have different freshness needs but share one pipeline | Split the consumers; let each have the cadence its tier requires |
The Redesign
The Result
- One nightly batch, 11 hours, $40 per run
- Tier 4 for everyone; tier 2 needs unmet
- Marketing built shadow pipeline that drifts from canon
- On-call paged 3-4 mornings a week
- Hourly micro-batch (5 min/run, $4) plus streaming micro-batch (1-min trigger, $200/day)
- Tier 4 for executive dashboard; tier 2 for marketing dashboard; both consumers happy
- One canonical fact table family; shadow pipeline retired
- On-call paged less than once a week; both paths run independently
What This Example Teaches
- ▸Has volume outgrown the available run window? If yes, run more often, not differently.
- ▸Is the transform non-incremental? If yes, make it incremental before changing engines.
- ▸Do consumers have different freshness needs? If yes, split the paths by tier.
- ▸Is the slowness in the transform itself or in the read/write paths? If reads and writes dominate, swapping the engine does not help.
> A growth-stage logistics company has a nightly batch that processes 80M delivery events and computes routes for the next morning. The pipeline is missing its 6am SLA and the cost has tripled in twelve months. The new tech lead is asked to redesign without rewriting everything as streaming.
Latency, throughput, state, and cost are the dimensions; pick deliberately, not by default
- Category
- Pipeline Architecture
- Difficulty
- intermediate
- Duration
- 30 minutes
- Challenges
- 0 hands-on challenges
Topics covered: Latency vs Throughput Tradeoff, Micro-Batch: The Middle Ground, Why Streaming Costs More, Stateful vs Stateless Transforms, When Batch Outgrows Itself
Lesson Sections
- Latency vs Throughput Tradeoff (concepts: paBatchVsStreaming)
Batch and streaming are usually framed as a single axis, fast versus slow. The framing hides the actual engineering decision, which has two axes. Latency is the time from event arrival to event being processed and visible. Throughput is how many events the pipeline can process per unit of time. The two are not the same and are often in tension: optimizing for one usually costs the other. A pipeline that processes one event in 100 milliseconds has low latency but may have low throughput because t
- Micro-Batch: The Middle Ground (concepts: paMicroBatchVsTrue)
Most production pipelines that look like streaming are not pure streaming. They are micro-batch: very small batches, often every few seconds or every minute, processed by an engine that exposes a streaming API on top. Spark Structured Streaming is the largest example. Flink can run in batch or streaming mode with a tunable trigger interval. The pattern exists because pure streaming is expensive and pure batch cannot meet sub-15-minute freshness. Micro-batch sits in the middle: latency low enough
- Why Streaming Costs More (concepts: paBatchVsStreaming)
Streaming costs more than batch for the same logic on the same data. The factor is rarely 10 percent; it is more often 5x to 50x. The cost difference is real and measurable, and it is the single most important variable in batch-versus-streaming decisions after freshness. Engineers who skip the cost conversation end up with streaming pipelines that consume budget the company does not want to spend, on freshness consumers do not need. The cost story has three components: continuous compute, state
- Stateful vs Stateless Transforms (concepts: paStreamProcessing)
Transforms divide into two categories that matter much more in streaming than in batch. A stateless transform processes one event at a time and produces output that depends only on that event. A stateful transform produces output that depends on more than one event: a count, a sum, a window, a join with another stream. The category changes the cost, the complexity, and the failure-recovery story. In batch, both categories look about the same because the engine has all the data in memory at once.
- When Batch Outgrows Itself (concepts: paBatchVsStreaming)
The exercise below walks through a real-shaped scenario: a pipeline that started as nightly batch, grew, and stopped meeting its freshness target. The redesign is not a wholesale switch to streaming. The redesign is a careful examination of which dimension is failing and the smallest change that fixes it. Most batch-to-streaming migrations in production look like this exercise, not like a rewrite. The Starting Pipeline An e-commerce company's nightly pipeline reads orders from a Postgres databas