Skip to main content

Apache Flink: Stream Processing, Windows & State Management

Apache Flink is a true streaming engine that processes events one at a time with sub-millisecond latency, fault-tolerant managed state, and powerful windowing semantics. The framework of choice for fraud detection, real-time analytics, and event-driven architectures at scale.

✓ True event-by-event streaming ✓ Event time + watermarks ✓ Exactly-once semantics ✓ Managed state (RocksDB)

What is Apache Flink?

Apache Flink is an open-source, distributed stream processing framework developed at TU Berlin and now one of the most active Apache projects. Unlike Spark Structured Streaming which processes data in micro-batches, Flink processes each event individually as it arrives, achieving consistent sub-millisecond latency. Flink provides exactly-once processing guarantees through distributed snapshots (Chandy-Lamport algorithm), making it reliable for financial and mission-critical applications.

Flink's key differentiator is managed state — stateful operations (running aggregates, session tracking, pattern matching) are stored in a configurable state backend and automatically checkpointed to durable storage. If a task fails, Flink restores state from the last checkpoint and replays from the source. Flink supports two primary APIs: the DataStream API for fine-grained control and Flink SQL for declarative streaming queries.

Window Types in Flink

DataStream API with Kafka Source

# PyFlink DataStream API — event-time windowed aggregation
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.connectors.kafka import KafkaSource, KafkaOffsetsInitializer
from pyflink.common import WatermarkStrategy, Duration
from pyflink.common.serialization import SimpleStringSchema
from pyflink.datastream.window import TumblingEventTimeWindows
from pyflink.common.time import Time

env = StreamExecutionEnvironment.get_execution_environment()
env.set_parallelism(4)

# Kafka source with event-time watermarks
source = KafkaSource.builder()     .set_bootstrap_servers("localhost:9092")     .set_topics("user-events")     .set_group_id("flink-consumer")     .set_starting_offsets(KafkaOffsetsInitializer.earliest())     .set_value_only_deserializer(SimpleStringSchema())     .build()

watermark_strategy = WatermarkStrategy     .for_bounded_out_of_orderness(Duration.of_seconds(5))     .with_timestamp_assigner(lambda event, _: extract_timestamp(event))

stream = env.from_source(source, watermark_strategy, "Kafka Source")

# Tumbling 1-minute window, keyed by user_id
result = (
    stream
    .map(lambda e: parse_event(e))          # (user_id, event_type, ts)
    .key_by(lambda e: e[0])                 # key by user_id
    .window(TumblingEventTimeWindows.of(Time.minutes(1)))
    .reduce(lambda a, b: (a[0], a[1], a[2] + b[2]))  # sum counts
)

result.print()
env.execute("User Event Aggregation")

Flink SQL Streaming Aggregations

-- Flink SQL — streaming join and windowed aggregation
-- Create Kafka source table
CREATE TABLE orders (
    order_id    STRING,
    user_id     STRING,
    amount      DOUBLE,
    event_time  TIMESTAMP(3),
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'kafka',
    'topic'     = 'orders',
    'properties.bootstrap.servers' = 'localhost:9092',
    'format'    = 'json'
);

-- Create output sink (e.g., Iceberg table)
CREATE TABLE order_summary (
    window_start TIMESTAMP(3),
    window_end   TIMESTAMP(3),
    user_id      STRING,
    total_amount DOUBLE,
    order_count  BIGINT
) WITH (
    'connector' = 'iceberg',
    'catalog-name' = 'hive_catalog',
    'database-name' = 'analytics'
);

-- Tumbling window aggregation (Flink SQL TVF syntax)
INSERT INTO order_summary
SELECT
    window_start,
    window_end,
    user_id,
    SUM(amount)  AS total_amount,
    COUNT(*)     AS order_count
FROM TABLE(
    TUMBLE(TABLE orders, DESCRIPTOR(event_time), INTERVAL '1' MINUTE)
)
GROUP BY window_start, window_end, user_id;

Stateful Processing with KeyedProcessFunction

# Stateful processing with Flink ValueState
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.functions import KeyedProcessFunction
from pyflink.datastream.state import ValueStateDescriptor
from pyflink.common.typeinfo import Types

class FraudDetector(KeyedProcessFunction):
    """Flag users with >3 transactions in 60 seconds."""
    def open(self, runtime_context):
        desc = ValueStateDescriptor("tx_count", Types.INT())
        self.tx_count = runtime_context.get_state(desc)

    def process_element(self, value, ctx):
        current = self.tx_count.value() or 0
        current += 1
        self.tx_count.update(current)

        if current > 3:
            yield f"FRAUD ALERT: user {value['user_id']} tx count {current}"

        # Register a cleanup timer (TTL alternative)
        ctx.timer_service().register_event_time_timer(
            ctx.timestamp() + 60_000  # 60 seconds
        )

    def on_timer(self, timestamp, ctx):
        self.tx_count.clear()  # reset count after window

Flink vs Spark Structured Streaming vs Kafka Streams

Feature Apache Flink Spark Structured Streaming Kafka Streams
Processing model True streaming (event-level) Micro-batch True streaming
Latency <1ms–10ms 100ms–seconds <1ms–10ms
State backend RocksDB / Heap In-memory / checkpoint RocksDB
Sources / sinks Kafka, S3, JDBC, custom Kafka, S3, Delta, custom Kafka only
SQL support Flink SQL (excellent) Spark SQL (excellent) None (Java/Scala DSL)
Deployment Standalone / YARN / K8s Spark cluster Library (no cluster)

Frequently Asked Questions

What makes Flink 'true streaming' vs Spark's micro-batch approach?▼

Spark Structured Streaming processes data in small batches (micro-batches) — it accumulates events for a configurable trigger interval (e.g., 1 second) then processes them as a batch. Latency is bounded by the trigger interval. Flink processes each event individually as soon as it arrives, with no batching delay. This makes Flink latency sub-millisecond in practice. The distinction matters for use cases like fraud detection, real-time bidding, or alerting where every millisecond counts. For analytics dashboards with 5-second refresh rates, Spark Structured Streaming is often simpler and sufficient.

What is the difference between event time and processing time in Flink?▼

Processing time is when the event arrives at the Flink operator — easy to implement but non-deterministic and cannot handle late-arriving data correctly. Event time is the timestamp embedded in the event itself (when the event actually occurred). Event time enables correct windowing even when events arrive out of order or delayed (e.g., mobile events buffered offline). Flink uses watermarks — monotonically increasing timestamps that tell the system 'all events with time <= watermark have arrived' — to advance event time and trigger windows. Event time with watermarks is the right approach for production streaming pipelines.

What are watermarks and how should I configure them?▼

A watermark is a marker in the data stream that says 'no event with timestamp earlier than this watermark will arrive.' Flink uses watermarks to decide when to close a time window and emit results. The WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(N)) strategy allows events up to N seconds late before the watermark advances. Set N too low and late events are dropped; set it too high and window latency increases by N seconds. Choose N based on your observed maximum network/processing delay. For very late events beyond N, use allowed lateness on the window to still update results after initial emission.

What is Flink state and why is it managed separately from the application?▼

Flink's managed state is stored in a configurable state backend (Heap, RocksDB) and automatically checkpointed to durable storage (HDFS, S3) at regular intervals. This decouples state from the JVM heap — RocksDB allows state far larger than available memory. When a job fails, Flink restores from the latest checkpoint, replaying only the events since that checkpoint from Kafka (or another replayable source). This gives exactly-once processing semantics without the application needing to implement recovery logic. State is also key-partitioned — each key's state lives on the operator processing that key.

When should I use Flink SQL vs the DataStream API?▼

Flink SQL is ideal for analytics-style streaming queries — windowed aggregations, streaming joins, CDC (Change Data Capture) processing. It is less code, easier to maintain, and benefits from Flink's SQL optimizer. The DataStream API gives full control for complex custom logic: machine learning inference, complex event processing (CEP), custom state machines, and operations that do not map cleanly to SQL. In practice, mix them — use Flink SQL for the transformation layer and DataStream API for custom sources, sinks, or logic that SQL cannot express.

What is Flink's savepoint and how does it differ from a checkpoint?▼

Checkpoints are automatic, periodic snapshots triggered by Flink for failure recovery. They are managed by Flink, can be overwritten, and are not intended for manual use. Savepoints are manually triggered, operator-addressable snapshots used for application upgrades, A/B testing, or migrating to a new Flink version. You can stop a job with a savepoint, deploy new application code, and restart from the savepoint — preserving all stateful context. This is critical for zero-downtime upgrades of long-running stateful streaming jobs. Savepoints use the same format as checkpoints but have stronger stability guarantees.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →