system-design · beginner

Batch vs Stream Processing — When to Wait, When to Flow

The Central Question

Consider an online checkout platform (checkoutlab.com) processing millions of global e-commerce purchases daily.

The engineering organization faces two fundamental data processing requirements:

  1. Requirement A (Real-Time Fraud Prevention): When a credit card transaction occurs, the system must analyze the user's spending velocity across the last 5 minutes and block stolen card attempts within under 200 milliseconds.
  2. Requirement B (Monthly Financial Audit): At the end of each calendar month, the accounting department must aggregate every purchase record, calculate regional sales tax liabilities, and generate financial reports across 500,000,000 historical rows.

If engineers attempt to solve Requirement A using nightly batch jobs, fraudulent cards cause millions of dollars in chargeback losses before the nightly job runs.

If engineers attempt to solve Requirement B by running raw real-time stream aggregations over 10 years of un-indexed historical logs, computational costs explode and financial audits become non-reproducible.

To process data effectively at scale, systems choose between Batch Processing and Stream Processing.

This lesson answers one central question: How do backend architectures trade off low-latency real-time stream processing over unbounded data against high-throughput batch processing over bounded historical data, and how do systems handle out-of-order events using Event-Time Watermarking, Tumbling/Sliding Windows, and Lambda vs Kappa Architectures?


Data Paradigms: Bounded Datasets vs. Unbounded Streams

The fundamental technical boundary between Batch and Stream processing is the nature of the underlying dataset:

flowchart TD
  DataParadigms[Data Processing Paradigms] --> Bounded[1. Bounded Datasets (Batch)]
  DataParadigms --> Unbounded[2. Unbounded Data Streams (Streaming)]
  
  Bounded --> BoundedDesc["FINITE Data Boundary.<br/>Fixed start and end point.<br/>Example: Yesterday's log file (00:00 to 23:59).<br/>High Latency (Hours), High Throughput."]
  Unbounded --> UnboundedDesc["INFINITE Continuous Stream.<br/>Has a start, but NO end point.<br/>Example: Live clickstream or transaction feed.<br/>Sub-second Latency, Continuous Compute."]

Figure 1: Comparison between Bounded finite datasets and Unbounded infinite streams.

Architectural Comparison Matrix

Vector / CharacteristicBatch ProcessingStream Processing
Data BoundaryBounded: Fixed size, finite dataset with clear start and end times.Unbounded: Continuous, infinite stream of arriving events.
Processing ParadigmStore-then-Process: Write data to disk, then execute query over full dataset.Process-in-Flight: Compute results continuously as events pass through memory.
Latency BenchmarkHigh Latency (Minutes to Hours).Ultra-low Latency (Milliseconds to Seconds).
Throughput & EfficiencyMaximizes Total Disk/CPU Throughput ($O(N)$ bulk scans).Optimized for Low Latency; state maintained in memory.
Primary FrameworksApache Spark, Hadoop MapReduce, Snowflake, BigQuery.Apache Flink, Kafka Streams, Apache Spark Streaming, Storm.

Windowing Strategies in Stream Processing

Because an unbounded data stream never ends, a stream processing engine cannot execute a global aggregate query like SELECT COUNT(*) FROM stream.

Instead, stream processors slice infinite streams into finite time slices called Windows:

flowchart TD
  Windows[Stream Processing Window Types] --> Tumbling[1. Tumbling Windows]
  Windows --> Sliding[2. Sliding Windows]
  Windows --> Session[3. Session Windows]
  
  Tumbling --> TDesc["Fixed-size, NON-overlapping time blocks.<br/>Example: [00:00-00:05], [00:05-00:10].<br/>Each event belongs to EXACTLY 1 window."]
  Sliding --> SDesc["Fixed-size, OVERLAPPING time blocks.<br/>Example: 5-minute window evaluating every 10 seconds.<br/>Events belong to MULTIPLE overlapping windows."]
  Session --> SessDesc["Dynamic gap-based windows.<br/>Example: User activity window closing after 15 minutes of inactivity.<br/>Variable duration per user session."]

Figure 2: Taxonomy of Tumbling, Sliding, and Session windowing strategies.

Mathematical Formulations of Windows

  1. Tumbling Window: Defines non-overlapping contiguous intervals of duration $W$. An event with timestamp $t$ maps to window index:
$$\text{Window Index} = \lfloor \frac{t}{W} \rfloor$$
  1. Sliding Window: Defines overlapping windows of length $W$ sliding at slide interval $S$ (where $S < W$). An event at time $t$ falls into $\frac{W}{S}$ simultaneous active windows!

Time Semantics: Event Time vs. Processing Time and Watermarking

In real-world distributed networks, mobile devices and web clients experience network latency, offline modes, and cell tower disconnects.

This creates a sharp conflict between two distinct timestamps:

timeline
    title Event Timeline: Out-of-Order Delivery Anomaly
    2026-07-23 12:00:00 : Event A Generated on Mobile Phone (Event Time)
    2026-07-23 12:00:01 : Event B Generated on Mobile Phone (Event Time)
    2026-07-23 12:00:02 : Mobile Phone Loses Cell Signal! (Network Partition)
    2026-07-23 12:00:05 : Server Processes Event B (Processing Time: 12:00:05)
    2026-07-23 12:00:15 : Cell Signal Restored! Event A Arrives Late! (Processing Time: 12:00:15)

Figure 3: Timeline showing how Event A occurs first in Event Time, but arrives late in Processing Time due to cell disconnects.

Definitions of Time Semantics

Resolving Late Data with Watermarks

If a stream processing engine aggregates metrics in 5-minute Tumbling Windows based on **Event Time**, how long should the window wait for late-arriving events (like Event A above) before closing and outputting the result?

Stream processing engines (such as Apache Flink) solve this using Watermarks.

A Watermark $W(t)$ is a monotonically increasing control metric emitted into the stream indicating that the system assumes all events with $\text{Event Time} \le t$ have been received:

flowchart LR
  Stream["Stream: [E(12:01), E(12:03), Watermark(12:05), E(12:04)]"] --> Engine[Stream Processor]
  Engine -->|Watermark 12:05 Arrives| Close[Trigger & Close 12:00-12:05 Window!]
  Engine -->|Late Event E(12:02) Arrives Later| Late[Route to Allowed Lateness / Side Output!]

Figure 4: Watermark propagation closing a stream processing window and routing late data to side outputs.

If an event arrives with an Event Time older than the current Watermark, it is treated as Late Data and routed to a Dead-Letter Queue or an Allowed Lateness side output to preserve window calculation reproducibility.


Architectural Systems: Lambda vs. Kappa Architectures

To balance real-time streaming needs against exact historical batch reporting, data engineering evolved two unified architectural frameworks:

flowchart TD
  subgraph Lambda Architecture: Dual Pipeline (Batch + Speed)
    L_Ingress[All Ingested Events] --> L_Speed[Speed Layer: Real-Time Stream Processor]
    L_Ingress --> L_Batch[Batch Layer: Immutable Append-Only Storage]
    
    L_Speed --> L_Views[Real-Time Fast Views]
    L_Batch --> L_BatchViews[Batch Comprehensive Views]
    
    L_Views --> L_Serving[Serving Layer: Query Merger]
    L_BatchViews --> L_Serving
  end
flowchart TD
  subgraph Kappa Architecture: Single Stream-Only Pipeline
    K_Ingress[All Ingested Events] --> K_Log[Durable Append-Only Event Log (Kafka / Pulsar)]
    K_Log --> K_Stream[Stream Processing Engine (Apache Flink)]
    K_Stream --> K_Views[Real-Time & Historical Views]
    
    Note1["Re-processing historical data is executed by simply REPLAYING the append-only event log!"]
  end

Figure 5: Structural comparison between dual-path Lambda Architecture and single-path Kappa Architecture.

Lambda vs. Kappa Comparison

VectorLambda ArchitectureKappa Architecture
Pipeline TopologyDual Path: Speed Layer (Stream) + Batch Layer (Hadoop/Spark).Single Path: Everything is a Stream (Apache Flink / Kafka).
Code DuplicationHigh: Developers must write business logic TWICE (Java/Scala for Flink, SQL/Python for Spark).Zero: Single codebase computes both real-time streams and historical log replays.
Historical ReprocessingRe-run batch job over cold storage HDFS/S3 data files.Re-wind consumer offset pointers on append-only stream log and re-stream.
Operational ComplexityHigh: Maintaining two distinct data engines and merging query results in the serving layer.Lower: Single stream processing engine manages all workloads.

Complete Worked Example: CheckoutLab Fraud Stream Processor

Let's examine a production Apache Flink stream processing pipeline for the CheckoutLab platform (checkoutlab.com).

// Production Apache Flink Stream Processing Blueprint (Java / DataStream API)
DataStream<OrderEvent> orderStream = env
    .addSource(new FlinkKafkaConsumer<>("orders.paid", new OrderSchema(), kafkaProps));

// 1. Assign Timestamps and Bounded Out-Of-Order Watermarks (5 Seconds Lateness Margin)
DataStream<OrderEvent> watermarkedStream = orderStream
.assignTimestampsAndWatermarks(
WatermarkStrategy
.<OrderEvent>forBoundedOutOfOrderness(Duration.ofSeconds(5))
.withTimestampAssigner((event, timestamp) -> event.getEventTimeEpochMs())
);

// 2. Group by User ID and Apply 5-Minute Sliding Window Evaluated Every 10 Seconds
DataStream<FraudAlert> alertStream = watermarkedStream
.keyBy(OrderEvent::getUserId)
.window(SlidingEventTimeWindows.of(Time.minutes(5), Time.seconds(10)))
.aggregate(new VelocityFraudAggregator());

// 3. Output Real-Time Fraud Alerts to Kafka Security Topic
alertStream.addSink(new FlinkKafkaProducer<>("security.fraud-alerts", ...));

Execution Strategy

The sliding window evaluates every user's purchasing velocity across the rolling 5-minute window. If a user executes more than 5 purchases totaling over $2,000 within 5 minutes, Flink outputs a `FraudAlert` event within **10 milliseconds** of the 5th transaction, automatically freezing the account.

Failure Modes and Engineering Mitigations

Failure ScenarioRoot CauseSystem SymptomDetection MetricMitigation Strategy
1. Un-Bounded State GrowthStream processor retains state for session windows without configuring State Time-To-Live (TTL).Stream worker nodes run out of heap memory and crash with Java OutOfMemoryError.RocksDB / Heap memory metrics spiking continuously over time.Enforce strict State TTL policies (StateTtlConfig.newBuilder(Time.hours(24))).
2. Silent Out-of-Order Data LossWatermark generator configured with zero lateness allowance (forMonotonicallyIncreasingTimestamps).Late-arriving mobile transaction events are silently dropped from window aggregations.Discrepancies between stream aggregations and nightly database audits.Configure realistic watermarking lateness bounds (e.g. 5s-60s) and route late data to side outputs.
3. Lambda Dual-Code DesyncBusiness rules updated in the Speed Layer stream code but forgotten in the Batch Layer code.Real-time dashboards report different revenue metrics than monthly accounting reports!Audit mismatches between real-time serving views and batch data warehouse views.Migrate to a Kappa Architecture to run identical code across real-time and historical replays.
4. Backpressure CascadeDownstream database sink slows down; stream processor cannot push output events.Stream worker buffers fill up; backpressure propagates upstream to Kafka topic brokers.High backPressuredTimeMsPerSecond metrics in stream processing operator UI.Apply async I/O database batching and scale sink database connection pools.

What You Should Remember

  1. Batch is bounded; Stream is unbounded: Batch computes over finite historical files; Stream computes continuously over infinite arriving event streams.
  2. Windowing structures infinite streams: Slice streams into finite compute blocks using Tumbling Windows (non-overlapping), Sliding Windows (overlapping), or Session Windows.
  3. Event Time reflects true business reality: Use Event Time (embedded payload timestamp) rather than Processing Time to evaluate window metrics correctly.
  4. Watermarks manage late data: Watermarks define the time boundary after which the stream engine assumes all past event-time data has arrived and closes the window.
  5. Kappa Architecture simplifies pipelines: Replace complex dual-path Lambda architectures with single-path Kappa stream replaying over append-only logs.

Glossary of Terms

TermDefinition
Batch ProcessingComputation executed over a complete, finite, bounded historical dataset on a scheduled basis.
Stream ProcessingContinuous computation executed over an infinite, unbounded real-time event stream as data arrives.
Event TimeThe timestamp embedded in an event payload recording when the event physically occurred on the source device.
Processing TimeThe local clock time of the stream processing node when it receives and evaluates an event.
WatermarkA control signal in a stream processor indicating that no events with earlier event-time timestamps are expected.
Tumbling WindowA fixed-size, non-overlapping time interval used to aggregate streaming events.
Sliding WindowA fixed-size, overlapping time interval that evaluates aggregations at a smaller slide frequency.
Kappa ArchitectureA system architecture that processes both real-time data and historical replays using a single stream processing engine.

Practice Scenario and Self-Assessment

Architecture Scenario

You are building the real-time ride demand surge pricing engine for a transportation platform (`ridelab.com`).

The engine must:

  1. Calculate the number of ride requests in each city neighborhood over the past 10 minutes.
  2. Recalculate surge multipliers every 15 seconds.
  3. Handle mobile rider requests that arrive up to 30 seconds late due to spotty tunnel connectivity.

Questions:
  1. Formulate the specific Windowing strategy (Window type, length, and slide) and Watermarking policy required for this engine.
  2. Explain why evaluating this engine using Processing Time creates invalid surge pricing during tunnel connectivity outages.


Interactive Self-Assessment

Events delayed by cell tower outages arrive at the server in a single burst, causing artificial metric spikes because Processing Time uses the server clock rather than when the event occurred.

Processing Time disables TCP socket connections on client mobile devices.

Processing Time converts all stream payloads into SQL DDL commands.

Processing Time revokes HTTPS SSL certificates on mobile browsers.

Kappa Architecture eliminates code duplication by using a single stream processing engine to compute both real-time metrics and historical log replays.

Kappa Architecture eliminates the need to maintain database backups.

Kappa Architecture doubles physical CPU clock speeds on processing nodes.

Kappa Architecture replaces public DNS nameservers with static hosts files.


What to Learn Next

Track: Software Design and Architecture

Previous: API Gateway — Edge Entry for Microservices

Next: Client–Server Architecture — Request Work From a Shared Machine

By Shubham Jain

All articles · Study paths

Shubham Jain · Learning Lab