Streaming vs Batch ETL: How to Choose on Cost and Latency

Streaming vs Batch ETL: How to Choose on Cost and Latency
Streaming vs Batch ETL: How to Choose on Cost and Latency

Teams often debate streaming versus batch ETL as though the choice starts with Kafka or a scheduler. It should start with a requirement: how old may data be when someone or something acts on it? The answer is rarely simply “real time.” A fraud-control decision, a stock-availability indicator and a finance dashboard have different tolerances for delay, partial data and correction.

The second question is economic. What must remain running to satisfy that freshness target, and how will the system behave when the input is idle, arrives in bursts or must be replayed? A faster pipeline is not automatically a more valuable pipeline. Conversely, a continuous pipeline can be economical when it replaces repeated heavy reads with incremental work. Both claims need workload-specific measurements.

This guide describes a way to select a processing cadence, model cost, and test correctness without treating vendor performance claims or list prices as universal results.

Batch, streaming and micro-batch are different dimensions

A batch pipeline operates over a bounded set of input, such as files in an object-store prefix or a database snapshot. It can run on a fixed schedule or in response to an event. A stream pipeline consumes an ongoing sequence of records and maintains progress as new data arrives. Neither label uniquely determines how often a downstream table is updated.

Streaming engines can also run for a finite period. Databricks explains the relationship between batch and streaming in its unified processing architecture: an incremental source can be processed continuously or on a triggered schedule. The same conceptual distinction applies beyond that product.

Micro-batching processes arriving records in short increments. It can serve near-real-time consumers without requiring record-by-record computations. Structured Streaming trigger documentation describes fixed processing-time intervals and an AvailableNow mode that consumes currently available input and then terminates. These alternatives illustrate why “we use streaming APIs” does not imply “our compute stays running forever.”

ETL and ELT describe where transformation happens, not necessarily when. Extract-transform-load can use batches or streams; extract-load-transform can ingest changes promptly but refresh a materialized result later. Separate these decisions before choosing technology.

Define a freshness budget that a consumer can test

Ask for the maximum acceptable delay between a source event and a usable downstream result. Specify the normal operating target and what happens during overload. Then name the consumer: a request handler, an alerting service, an analyst or a scheduled reconciliation job.

A practical freshness contract contains four parts:

  • Origin: the clock or event timestamp used to determine when a change occurred.
  • Destination: the precise table, API or decision point where the change must be visible.
  • Tail behavior: the percentile or worst-case bound that matters, including catch-up after failure.
  • Correction policy: whether consumers accept preliminary answers that may change when late data arrives.

The measurement should include source capture, queueing, transformation, sink commit and query visibility. Checking only processor execution time misses the backlog waiting outside the processor. An idle dashboard can look fast while its upstream connector is hours behind.

The distinction between event time and processing time matters. In the Apache Beam programming guide, watermarks represent the system's estimate of event-time progress; they do not prove that no older event will appear later. Triggers and allowed lateness let a pipeline publish early results and revise them when delayed records show up.

For example, an illustrative operations dashboard might show a provisional minute-level order count quickly and reconcile it after delayed transactions arrive. A financial statement may instead prefer a later, stable result. These are different correctness contracts, even if both ingest the same records.

Model the whole bill, not just the ingestion fee

A useful monthly total-cost model includes ingestion, transport, transformation compute, state storage, data storage, orchestration, serving queries, monitoring and operations. Add change management and on-call burden as explicit categories rather than hiding them in a platform fee.

For a batch job, estimate compute per run multiplied by runs per period, plus startup overhead and the storage used to stage data. For a continuously running job, estimate active capacity across the full window, autoscaling behavior, checkpoint storage and request/byte-based ingestion fees. Neither formula works unless it accounts for retries, backfills and idle input.

Provider pricing demonstrates why the implementation details matter. BigQuery pricing distinguishes batch load jobs using the default shared pool from streaming ingestion methods, and it bills stored data independently. “Batch loading is free” does not mean the complete data pipeline is free: storage, query processing, upstream object storage and some capacity arrangements can still be billed.

Dataflow pricing likewise lists different resource meters for batch and streaming workers, along with additional service and storage charges. Comparing two services by their per-byte ingest rate without including running compute is therefore misleading.

Traffic shape changes the answer. Sparse events with long idle periods can suit scheduled incremental runs if the scheduler can release compute. A steady stream with costly repeated scans may favor a continuously incrementally maintained result. Transport cost is another variable: Kinesis pricing documents provisioned capacity billing as well as on-demand approaches. Actual cost depends on traffic and configuration rather than a universal streaming premium.

Stateful correctness has a real operating cost

A stateless format conversion is simpler than a rolling aggregation, joining two delayed streams, or deduplicating events across retries. Stateful processing needs a policy for memory, expiration, snapshots, restart behavior and historical recomputation.

Apache Flink's checkpointing documentation explains how persistent state snapshots support recovery and how exactly-once and at-least-once modes differ. A checkpoint does not by itself make a non-transactional external API exactly-once. The sink must coordinate writes or provide another safe deduplication mechanism.

Similarly, Kafka delivery semantics distinguish retries, at-least-once delivery, transactional Kafka-to-Kafka processing and the extra coordination needed when writing to external systems. The useful architecture question is not “Does Kafka support exactly once?” but “Can the whole source-to-sink operation be retried without corrupting the business result?”

Batch has recovery complexity too. A failed job may partially write output, overlap the next scheduled run, or rerun against a changed snapshot. Idempotent writes, immutable partitions, atomic publish steps and audit records matter in both modes. Continuous streaming makes some of those problems more visible because recovery happens while new events keep arriving.

Calculate effective freshness, not nominal schedule

A scheduled job that starts every few minutes may appear to have predictable latency, but its actual freshness depends on when the event arrives relative to the schedule, job startup, runtime and commit. Events arriving just after a run's input cutoff wait for the next eligible run. A continuously operating job eliminates that particular scheduling wait but still experiences queueing, watermark delays, database commit times and sink visibility delays.

For a rough engineering model, break total delay into source-capture delay, waiting-to-process, processing, and visible-at-destination delay. Track the observed distribution of each component rather than inventing a single system-wide constant. A large scheduled interval may dominate freshness for low-volume data. A small interval cannot compensate for a persistently saturated sink.

A second distinction is detection latency versus completeness latency. An alert that one suspicious event occurred can often fire before all related events have arrived. A per-customer total, deduplicated across devices and time zones, may need event-time windows and a documented correction window. Treating those two outputs as equivalent leads either to expensive unnecessary work or misleading early answers.

A simple service-level test uses synthetic records carrying a source timestamp and a unique ID. Follow each one through connector ingestion, processor, storage commit and the final query endpoint. Measure both normal and degraded periods; compare observed age to the agreed deadline. Also record how long it takes to catch up after stopping input consumption temporarily. The catch-up result may matter more than idle-state latency.

A worked cost worksheet without imaginary cloud prices

Suppose a product produces a small event stream that is quiet at night and bursty after a campaign. It has two downstream consumers: an operational alert and an analytical report. This is an illustrative scenario, not a measured company deployment.

For scheduled incremental ETL, total cost can be represented as:

Batch total = scheduled compute + startup overhead + staging/storage + transport + serving + retries/backfills + operational labor.

For an always-on architecture:

Streaming total = continuously allocated or metered compute + event transport + ingestion/write charges + checkpoint/state storage + serving + replays + operational labor.

If the service scales to zero between jobs, count the shutdown benefit and its startup penalty. If it cannot scale to zero, include idle hours. If a stream processor charges for reserved minimum capacity, account for that floor even during empty periods. If a data warehouse meters storage writes independently of transformation workers, calculate each bill separately rather than burying both in “ingest.”

Use the same workload and billing period for both options. Keep query workloads, retention and data quality rules equal. Run both under typical traffic, peaks, retries and backfills. The result to compare is not simply dollars per gigabyte; it is dollars per correct, consumer-visible update meeting its deadline, alongside recovery and support requirements.

Cost estimates should include uncertainty ranges for traffic growth, discounts, cross-region transfer and unanticipated reprocessing. A decision that changes under modest forecast variation is not a strong argument for a complicated migration.

A decision process that avoids false precision

Start with the least demanding cadence that satisfies the measured freshness SLO, then benchmark a lower-latency option if there is evidence of additional value. Consider three candidate designs: scheduled batch, triggered incremental micro-batch and continuous streaming.

Run representative workloads, including idle periods, normal traffic, spikes, invalid records and failures. Measure end-to-end freshness, backlog recovery, cost per useful update, data correctness and operational effort. A fast median with a long recovery tail may be a poor fit for an alerting use case.

Separate indispensable low-latency paths from work that can be deferred. For example, an illustrative commerce platform might update an availability signal on each relevant event, while recalculating a detailed profitability report later. That avoids making every downstream transformation inherit the tightest latency requirement.

Finally, preserve a backfill path and a source of replayable truth. A streaming pipeline that cannot reconstruct a lost table is not operationally complete; a batch pipeline that silently skips a failed window is no better. The best choice is the design that satisfies freshness and correctness requirements at an acceptable, measured total cost.

Practical architecture patterns

Scheduled incremental ingestion

Keep an immutable landed-data area or replayable change log. Run an incremental reader on a scheduler; track a durable cursor or completed input interval, then write new records idempotently. Publish a completion marker only after target data has been committed. Alert when the cursor stops advancing or a batch overlaps its successor.

This pattern works when consumers accept scheduled updates, but careless cursor handling can cause gaps. The cursor should advance with committed output, not merely with a successful read. Make each batch independently retryable and retain enough source history to rerun it safely.

Micro-batch using a streaming engine

A micro-batch engine can reuse a streaming source connector and checkpoint while launching work on a schedule. The Databricks AvailableNow trigger is one documented example: it handles the data available at trigger time and stops once that work is complete. This approach can reduce fixed compute occupancy when an engine supports efficient startup and shutdown; that is a conditional benefit rather than a guaranteed saving.

Watch for jobs that routinely take longer than the launch interval. They form overlapping backlogs, not near-real-time pipelines. Also check whether autoscaling and cluster warm-up undermine the intended cost advantage.

Continuous stateful processing

Use a sustained processing path when results must be updated as events arrive and a periodic job cannot meet the deadline. Partition by a stable key when order per entity matters. Set state expiration policies, watermark and late-event handling, external sink idempotency and checkpoint durability deliberately. Maintain a replay procedure for corrupted derived state.

This design is appropriate for genuinely time-sensitive signals, but different downstream consumers can still use a slower cadence. Exposing everything through the same continuous graph may couple operationally independent tasks.

Direct row ingestion versus file ingestion

Snowflake describes Snowpipe Streaming as a row-oriented ingestion path, while file-based Snowpipe suits workloads that already deliver objects to cloud storage. Its published latency and throughput statements describe product capabilities, not an independent guarantee for every customer's schema, transformation and query path.

The architecture comparison must include whether the source already produces files, how connectors handle retry and ordering, whether ingestion itself is the bottleneck, and when the final table becomes queryable. A direct row path is not automatically a reason to rebuild the rest of a dependable file-based pipeline.

Measure the failure modes before launch

Use a controlled acceptance exercise that answers the following questions:

  1. Duplicate delivery: if the same input is re-sent, does the destination maintain the correct logical result?
  2. Late arrivals: if an event arrives after its event-time window, does the output revise, reject or quarantine it as specified?
  3. Backpressure: if the sink slows, does source lag grow predictably and recover when capacity returns?
  4. Restart: if a processor fails after a write but before checkpoint confirmation, what is replayed?
  5. Backfill: can the team reprocess historical data without exposing partial or double-counted output?
  6. Schema change: do incompatible records fail visibly rather than disappear from the result?

Instrument source offset or cursor age, event-time lag, input/processing rates, write failures, state size, checkpoint duration and the final consumer-visible delay. Apache Spark's streaming query monitoring guidance documents progress reports with input rates, processing rates and trigger timing. These are useful processor signals, but they do not replace checking what consumers actually see.

A replay must be more than “restart the process.” Define the starting offset, output replacement strategy, data-retention horizon and audit trail. For high-value financial or security decisions, compare replayed outputs with an independently computed reconciliation rather than assuming a green runtime status proves correctness.

The practical default is a measured hybrid

The useful boundary is often between urgent decisions and everything else, not between Kafka and cron. Continuously capture the changes that require prompt action, then use controlled incremental or scheduled transformations where tighter freshness has no demonstrated benefit.

Treat the initial architecture as a testable hypothesis. Establish an end-to-end freshness objective, calculate the full bill, inject failure, test late data and examine replay behavior. Only then can a team justify paying for lower latency—or safely deciding it does not need it.