Skip to content

Phase 9: Real-World Choke Points, Capacity Planning & Telemetry

Audience: Principal Site Reliability Engineer & Distributed Systems Performance Specialist
Source References: flink-runtime/src/main/java/org/apache/flink/runtime/metrics/


1. Production Choke Points & Root Cause Pathologies

1.1 Checkpoint Alignment Timeout Disaster

When backpressure builds, network channels fill with data records. - In the Flink Web UI under Checkpoints -> Details: - Sync Duration: 2 ms - Async Duration: 400 ms - Alignment Duration: 58,400 ms (Exceeds 60s timeout!) - Symptom: Checkpoints fail repeatedly with CheckpointExpiredException. Flink restarts the entire streaming job, causing an infinite crash-and-restart loop! - Fix: Enable Unaligned Checkpoints (enableUnalignedCheckpoints()).


1.2 Data Skew on keyBy()

When data is partitioned by key (stream.keyBy(...)), an uneven distribution of keys (e.g. 80% of events belong to tenant_id: "VIP_CUSTOMER") causes a single Task Slot to receive 80% of total workload:

Data Skew Telemetry in Web UI:
Subtask 0: busyTime = 4%,   recordsIn = 1,000 /sec
Subtask 1: busyTime = 3%,   recordsIn = 1,200 /sec
Subtask 2: busyTime = 99%,  recordsIn = 95,000 /sec  <=== SKEW BOTTLENECK!
Subtask 3: busyTime = 5%,   recordsIn = 1,100 /sec

The Two-Stage Salt Aggregation Pattern:

// Phase 1: Local Pre-Aggregation with Random Salt (e.g. 0 to 9)
DataStream<AggregatedResult> preAgg = source
    .map(event -> new SaltedEvent(event, random.nextInt(10)))
    .keyBy(SaltedEvent::getSaltedKey) // Evenly spreads across ALL 4 subtasks!
    .window(TumblingEventTimeWindows.of(Time.minutes(1)))
    .aggregate(new PartialSum());

// Phase 2: Global Merge of the 10 Partial Results
DataStream<FinalResult> finalResult = preAgg
    .keyBy(PartialResult::getOriginalKey)
    .window(TumblingEventTimeWindows.of(Time.minutes(1)))
    .aggregate(new FinalSum());

2. Diagnostics: Thread Dumps & CPU Flame Graphs

The Flink Web Dashboard provides native, zero-downtime profiling tools:

2.1 Thread Dump Analysis

Click TaskManagers -> Select Worker -> Thread Dump: - Look for threads in TIMED_WAITING or BLOCKED:

"Task Slot-1" prio=5 tid=0x7fff0 nid=0x1803 runnable
  at org.rocksdb.RocksDB.get(Native Method)
  at org.apache.flink.contrib.streaming.state.RocksDBValueState.value()
(Indicates the operator is stalled waiting for slow disk I/O on RocksDB read!)

2.2 CPU Flame Graphs & Kryo Serialization Tax

Enable Flame Graphs in the Web UI: - If a large portion of the flame width is occupied by com.esotericsoftware.kryo.*: - The Pipeline is Trapped in Fallback Serialization! - Flink could not determine a strict POJO schema and fell back to slow generic Java reflection (Kryo). - Fix: Enforce POJO conventions (public getters/setters, default no-arg constructor) or register custom Avro/Protobuf serializers to eliminate 80% of CPU overhead!


3. Backpressure Monitoring Dashboard

Flink Backpressure Visual Indicators:
[ Source ] ──► [ Transform ] ──► [ Aggregate ] ──► [ Sink ]
  [RED]            [RED]             [RED]          [GREEN]
 (Backpressured)  (Backpressured)   (Bottleneck)     (Idle)
  1. RED Status: Task is experiencing \(> 50\%\) backpressure.
  2. Follow the red chain downstream.
  3. The last red operator in the chain is the root bottleneck! Inspect its external dependencies (e.g. slow database writes, locks, garbage collection).