Skip to content

Phase 4: Fault Tolerance, Checkpoints & Exactly-Once Semantics

Audience: Principal Distributed Systems Architect & Fault Tolerance Specialist
Source References: flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/, flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/sink/TwoPhaseCommitSinkFunction.java


1. Asynchronous Barrier Snapshotting (ABS Algorithm)

Flink achieves distributed fault tolerance using an optimized variant of the Chandy-Lamport Distributed Snapshot Algorithm:

ABS Checkpoint Execution Flow:
1. CheckpointCoordinator (JobManager) periodically generates CheckpointID (e.g. 501).
2. Coordinator sends RPC to all Source operators: TriggerCheckpoint(501).
3. Sources capture their current read offsets (e.g. Kafka partition offsets).
4. Sources inject CheckpointBarrier(501) into output data streams.
5. Barriers flow downstream interleaved with data records.
Stream Flowing Through Operator:
[Record A] ──► [Record B] ──► [CheckpointBarrier 501] ──► [Record C] ──► [Record D]
                                       │
                                       ▼
                     Delineates State: Records before Barrier
                     are included in Checkpoint 501. Records after
                     Barrier belong to Checkpoint 502!

2. Aligned vs. Unaligned Checkpoints

┌────────────────────────────────────────────────────────────────────────┐
│ 1. Aligned Checkpoints (Default)                                       │
├────────────────────────────────────────────────────────────────────────┤
│ - Operator waits until Barrier N arrives on ALL parallel input channels│
│ - Records arriving on Channel 1 are buffered in RAM while waiting for  │
│   Channel 2's barrier.                                                 │
│ - Flaw: Under high backpressure, Barrier N is stuck behind thousands   │
│   of records in network buffers, causing alignment timeout failures.   │
└────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────┐
│ 2. Unaligned Checkpoints (High-Throughput / Backpressure Resilient)    │
├────────────────────────────────────────────────────────────────────────┤
│ - The instant Barrier N arrives on ANY channel, it JUMPS the queue!    │
│ - Operator snapshots its internal state PLUS all in-flight buffers!    │
│ - Zero alignment wait time! Completes in seconds under 100% backpressure│
└────────────────────────────────────────────────────────────────────────┘

Production Configuration:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(10000, CheckpointingMode.EXACTLY_ONCE);

CheckpointConfig config = env.getCheckpointConfig();
config.enableUnalignedCheckpoints(); // Jump network queues under backpressure
config.setAlignmentTimeout(Duration.ofSeconds(2)); // Try aligned for 2s, then switch!
config.setMinPauseBetweenCheckpoints(5000); // Prevent checkpoint storm starvation
config.setCheckpointTimeout(60000);

3. End-to-End Exactly-Once with Two-Phase Commit (2PC) Sinks

Generating correct internal state snapshots is useless if the external storage sink (e.g. Kafka, PostgreSQL) writes duplicate records on recovery.

To achieve True End-to-End Exactly-Once, Flink integrates TwoPhaseCommitSinkFunction:

sequenceDiagram
    autonumber
    participant App as "Flink Worker Task"
    participant Sink as "TwoPhaseCommitSink (Kafka Sink)"
    participant Kafka as "Kafka Broker (Transactional Coordinator)"
    participant Coord as "JobManager (CheckpointCoordinator)"

    Note over App,Sink: Normal Streaming Phase
    App->>Sink: Process records
    Sink->>Kafka: Write records under Transaction T1

    Note over Coord,Sink: Phase 1: Pre-Commit (Checkpoint Triggered)
    Coord->>App: Deliver CheckpointBarrier(100)
    App->>Sink: Forward Barrier(100)
    Sink->>Kafka: Flush in-flight records and Pre-commit Transaction T1
    Sink->>Sink: Open new Transaction T2 for incoming records!
    Sink->>Coord: Acknowledge Checkpoint 100 Pre-Committed

    Note over Coord,Sink: Phase 2: Commit (Global Checkpoint Committed)
    Coord->>Sink: NotifyCheckpointComplete(100)
    Sink->>Kafka: Commit Transaction T1! (Records visible to consumers!)

3.1 What Happens on Failure Recovery?

If a TaskManager crashes before Step 8: 1. JobManager restarts the job from Checkpoint 99. 2. The uncommitted Transaction T1 in Kafka times out and is aborted by the broker. 3. Flink replays the exact stream from Checkpoint 99's offsets. 4. Result: Zero duplicates, zero data loss, exact mathematical fidelity!