Stream Processing with Apache Flink: Real-Time Pipelines for the Modern Data Engineer
November 16, 2025 · 10 min read
Streaming used to be a specialization. In 2026 it is table stakes. Senior data engineers are expected to design systems that do not wait for nightly batches to tell the business what is happening. Fraud, personalization, operational alerting, and usage analytics all demand pipelines that react in minutes or seconds. If you can reason about late events, checkpointing, and backpressure, you are already in the top tier of modern data roles.
Apache Flink is the tool that repeatedly shows up when a team needs reliable stateful processing at scale. It is not the only option, but it is the one that forces you to think in terms of event time, deterministic state, and operational correctness. That mindset is what separates a “streaming job that works in dev” from a real-time platform that leadership can trust.
Flink vs Spark Structured Streaming vs Kafka Streams
Use Flink when you need long-lived state, complex event-time windows, or high-throughput joins that must remain correct under backpressure. Flink was designed for unbounded streams and can run for months with consistent state snapshots. It is also the best fit when you need exactly-once semantics across Kafka, filesystems, and lakehouse tables.
Spark Structured Streaming is a pragmatic choice when your team is already deep in Spark and you want a unified batch and streaming engine. Micro-batch is a good compromise for many analytics use cases, and Spark has mature integrations with Delta Lake, Iceberg, and most data warehouses. If your latency target is measured in minutes and you want to reuse Spark SQL logic, Spark can be the right answer.
Kafka Streams shines for embedded streaming inside JVM services. It is a library, not a cluster, so it is easier to deploy but limited by the resources of the service it runs in. Use Kafka Streams for localized transformations, lightweight enrichment, or when the operational overhead of a separate streaming cluster is not justified.
Flink Architecture: JobManager, TaskManager, State
Flink separates control and execution. The JobManager coordinates deployments, tracks checkpoints, and handles failure recovery. The TaskManagers execute the actual operators, manage state, and exchange data over network shuffle. In production you will run multiple TaskManagers with several slots each, giving Flink the parallelism it needs to scale.
Checkpointing is Flink's backbone. Each operator periodically snapshots state to a durable backend. If a node fails, Flink restores from the latest checkpoint and replays events to maintain exactly-once processing. State backends matter here. RocksDB is the default for large state because it spills to disk, while the in-memory backend can be faster for smaller workloads. Choosing the right backend is an early architectural decision that impacts cost and recovery time.
DataStream vs Table API, Event Time vs Processing Time
The DataStream API gives you the most control. You build pipelines of map, keyBy, process, and window operators and manage state explicitly when needed. The Table API and SQL layer sit on top and let you express transformations declaratively. For analytics teams that think in SQL, the Table API is often the fastest path to production, while the DataStream API is best for custom logic.
Event time is the only time that matters for correctness. Processing time is convenient but it lies when events arrive late or out of order. Flink handles event time with watermarks, which represent how far the pipeline believes it has progressed in the event-time domain. Watermarks allow windows to close deterministically and still accommodate late arrivals with defined allowed lateness. Mastering this mental model is what makes Flink feel powerful.
Kafka → Flink → Iceberg or Delta Lake
The most common modern pattern is a streaming lakehouse. Kafka captures events, Flink cleans and enriches them in real time, and Iceberg or Delta Lake stores the curated results as append-only tables with snapshot isolation. This gives you both low-latency analytics and reproducible batch queries.
The key is to keep your streaming tables idempotent. Flink can write to Iceberg or Delta with exactly-once sinks, but you must ensure deterministic keys and stable schema evolution. When done right, your streaming tables become the single source of truth that both dashboards and backfills can depend on.
PyFlink Example: Word Count, Then Sessionized Events
The classic PyFlink word count still teaches the core concepts. You set up a DataStream, key by word, then aggregate in a window. It is trivial, but it introduces the operator graph and stateful aggregation patterns that scale.
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.common import Types
env = StreamExecutionEnvironment.get_execution_environment()
text = env.from_collection([
"flink makes stateful streaming reliable",
"stream processing is now table stakes"
])
counts = (
text.flat_map(lambda line: line.split(" "), output_type=Types.STRING())
.map(lambda word: (word, 1), output_type=Types.TUPLE([Types.STRING(), Types.INT()]))
.key_by(lambda item: item[0])
.sum(1)
)
counts.print()
env.execute("word-count")In production, the real work is sessionization. You ingest user events from Kafka, assign event time, and build session windows keyed by user ID. That gives you aggregates like session length, page depth, and conversion funnels in near real time. This pattern is the backbone of modern product analytics pipelines.
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.common.time import Time
from pyflink.datastream.window import EventTimeSessionWindows
stream = env.from_source(kafka_source, watermark_strategy, "events")
sessionized = (
stream.key_by(lambda event: event.user_id)
.window(EventTimeSessionWindows.with_gap(Time.minutes(30)))
.aggregate(session_agg_fn)
)
sessionized.add_sink(iceberg_sink)Production Considerations: Exactly-Once, Backpressure, Monitoring
Exactly-once semantics are only real if every sink is configured to honor them. For Kafka this means transactional producers and idempotent writes. For Iceberg or Delta, it means using the Flink sink that integrates with table commits rather than writing files directly. Treat the whole path as a single contract.
Backpressure is your early warning system. When operators downstream cannot keep up, Flink slows upstream sources. This is expected, but it needs observability. The Flink Web UI gives operator-level metrics, and exporting metrics to Prometheus lets you set alerts on checkpoint duration, task idle time, and queue depth. If you cannot see backpressure, you will discover it in the form of growing lag and missed SLAs.
When Flink Is Overkill
Flink is powerful, but it is not always necessary. If your latency requirement is five minutes, Spark Structured Streaming is simpler and usually cheaper to operate. If you need basic enrichment or filtering on a Kafka topic, Kafka Streams inside a service might be enough. The wrong move is to build a Flink cluster just because streaming feels impressive. Choose Flink when the workload truly needs event-time correctness, large state, or continuous low-latency processing at scale.
Closing
The strongest data platforms treat streams as first-class citizens. Flink forces that discipline by making time, state, and correctness explicit. If you can design a reliable Flink job, you can design almost any streaming system. That is why streaming expertise has become a senior-level expectation and why Flink remains the most credible signal of real-time engineering depth.
If you are evaluating your stack, start by defining the business latency target and the failure modes you can tolerate. If the answer is “we need correct, low-latency stateful processing,” you already know where Flink fits.
Questions or pushback on any of this? Find me on LinkedIn.
Ryan Kirsch is a senior data engineer with 8+ years building data infrastructure at media, SaaS, and fintech companies. He specializes in Kafka, dbt, Snowflake, and Spark, and writes about data engineering patterns from production experience. See his full portfolio.