When Batch Outgrows Itself

Batch pipelines outgrow themselves in 2 ways, and the fix depends on which. The data grows until the run no longer finishes inside its schedule, a throughput problem. Or the consumer's freshness need shrinks until the schedule itself is too slow, a latency problem. Both show up as the same complaint, the dashboard is behind, and both tempt a team to rebuild as streaming. Name the problem first, then climb the smallest number of rungs that meets the consumer's tier.

Consumer freshness tierSmallest pattern that meets itCost relative to nightly batch
Yesterday's numbers (24 h)Nightly batch1x
This hour's numbers (1 h)Hourly batch over new rows only1x to 2x
Within a few minutesMicro-batch, 1 to 5 min trigger3x to 10x
Within secondsStreaming, stateless where possible15x and up

A worked diagnosis

A nightly job rebuilds a 2 year curated.orders table from the full raw history every night at 01:00. 2 years ago it took 90 minutes. It now takes 5 hours 40 minutes and must finish before 07:00, when the finance team opens its dashboard. Meanwhile the operations team has asked for the same table within 1 hour so it can react to regional delivery delays. Run time growing in step with history is the throughput signal; the 1 hour request is the latency signal. The job has both problems, and the same rung fixes both.

Most of the job's work is reprocessing 730 days of orders to add 1 day's worth. Switch it to process only rows that arrived since the previous run, and a nightly run reads 1 day of rows instead of 730. Once runs are small, the schedule can tighten to hourly without touching streaming. Each hourly run handles around 170,000 rows in about 3 minutes, so worst-case latency falls from 30 hours to around 63 minutes, which the operations team accepts as its hourly tier.

INSERT INTO curated.orders
SELECT
  order_id,
  customer_id,
  city,
  amount,
  event_ts,
  ingested_ts
FROM raw.order_events
WHERE ingested_ts > (
  SELECT
    coalesce(max(ingested_ts), TIMESTAMP '1970-01-01 00:00:00')
  FROM curated.orders
)
One hourly run feeds both consumers from the same curated table.
  • Raw order events (source, Raw table)
  • Hourly load (transform, Scheduled, 1 h); freshness: under 65 min; parallelism: 8 partitions
  • Curated orders (storage, Curated table)
  • Finance dashboard (consumer, Opened 07:00)
  • Ops delay board (consumer, Needs 1 h)
  • Raw order events -> Hourly load
  • Hourly load -> Curated orders
  • Curated orders -> Finance dashboard
  • Curated orders -> Ops delay board

Moving one path to micro-batch or streaming

Stop climbing when the tier is met. If the operations team later needs delays within 2 minutes, the next step is a micro-batch job on the order queue with a 60 second trigger, and only the path feeding the delay board moves. Finance keeps its hourly load: a fresher table gains it nothing and would cost 3 to 10 times more. If a fraud consumer later needs sub-second decisions, that single stateless check becomes a streaming job, while the stateful aggregates stay in micro-batch where state and recovery cost less.

Do
  • Name the constraint first: a growing backlog is throughput, a waiting consumer with idle machines is latency
  • Process only new rows before shortening the schedule
  • Move one consumer's path up a rung, never the whole table
Avoid
  • Shorten a schedule on a throughput-bound job; extra runs add overhead, not speed
  • Rebuild as streaming because the dashboard is behind
  • Put a stateful aggregate into a stream before pricing its state and checkpoints

A daily batch loads 20M click rows in 50 minutes for a marketing dashboard viewed at 09:00. Product wants a live board of clicks per page over the last 5 minutes, refreshed within 1 minute. Marketing's need is unchanged. Which design is the smallest that meets both?

About this lesson

This section is part of the Batch vs Streaming: Intermediate lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.