Back to Apache Flink
Q21Question 21 of 33

How do you monitor Flink jobs and what metrics indicate trouble

Key Concepts
  • The Flink Web Dashboard shows the job graph, per-operator backpressure, checkpoint history, watermark progress, and recent exceptions.
  • The Flink metrics system exports time series like records-in/out per second, latency, checkpoint duration, state size, and buffer usage to Prometheus or Grafana.
  • Steadily increasing checkpoint duration means state is growing or the state backend is under pressure, risking checkpoint stalls.
  • Continuously growing state with no plateau signals a state leak, often keyed state that is never cleaned up.
  • Backpressure concentrated on one operator identifies the bottleneck; everything upstream slows behind it.
  • Rising Kafka consumer lag means the job is slower than the input rate, tying back to backpressure or insufficient parallelism.
  • Alert on trends and rates of change rather than only absolute values for early warning before outages.

Definition

Use the Flink Web Dashboard for live inspection and export Flink metrics to Prometheus or Grafana, watching checkpoint duration, state size, backpressure, and consumer lag as the key danger signs.
  • Flink Web Dashboard – A built-in UI served by the JobManager that visualizes the job graph, per-operator backpressure, checkpoint history, watermarks, and exceptions.
  • Flink Metrics System – Exports numeric time series through reporters (Prometheus, Datadog) for dashboards, historical trends, and alerting.
  • Key throughput metrics – numRecordsInPerSecond and numRecordsOutPerSecond per operator.
  • Key checkpoint metrics – lastCheckpointDuration, lastCheckpointSize, and completed/failed checkpoint counts.
  • Key state metrics – State size per operator, visible via checkpoint size.
  • Key backpressure metrics – busyTimeMsPerSecond, backPressuredTimeMsPerSecond, idleTimeMsPerSecond per operator.
  • Consumer lag – For Kafka sources, records-lag-max tells whether the job is keeping up with input.

Running a Flink job at the National Bank of India is like running the payments processing line in a branch. The Web Dashboard is the live camera feed on the wall: you see each station, whether any station has a queue forming behind it, and whether the overnight safe-backup finished on time. The metrics system is the set of sensors wired to head office (Prometheus and Grafana), recording throughput, delays, and backup times minute by minute, with alarms that page the on-call engineer when something trends the wrong way.

Understanding the Concepts

The Web Dashboard, served by the JobManager, is the first stop for live inspection. It shows the logical and physical job graph, per-operator parallelism, backpressure status, checkpoint history, watermark progress, and recent exceptions.

TEXT
Flink Web Dashboard tabs:

  Overview:     Job graph with operator parallelism
  Backpressure: Per-operator backpressure status (colored)
  Checkpoints:  History, duration, size, success/failure
  Watermarks:   Event-time progress per operator
  Exceptions:   Recent failures and restarts
  Metrics:      Raw metric values per operator

Access: http://jobmanager-host:8081
JAVA
package com.bank.monitoring;

// The Web Dashboard is built-in, no code needed
// Access at: http://jobmanager:8081
//
// Key views for NBI fraud pipeline:
// 1. Job graph: verify operator chain and parallelism
// 2. Backpressure tab: spot which operator is bottleneck
// 3. Checkpoints tab: verify checkpoints are completing
// 4. Exceptions tab: check for recent failures

How do you export metrics to Prometheus and Grafana?

Flink exposes metrics through reporters. The most common setup uses the Prometheus reporter, which publishes an endpoint that Prometheus scrapes. Grafana then builds dashboards and alerts from the scraped data.

TEXT
# flink-conf.yaml: enable Prometheus reporter
metrics.reporter.prom.factory.class:
  org.apache.flink.metrics.prometheus.PrometheusReporterFactory
metrics.reporter.prom.port: 9249

# Prometheus scrape config:
scrape_configs:
  - job_name: 'flink'
    static_configs:
      - targets: ['taskmanager-1:9249', 'taskmanager-2:9249']
JAVA
package com.bank.monitoring;

public class PrometheusMetricsDemo {
    // Key Prometheus queries for Grafana dashboards:
    //
    // Throughput:
    //   flink_taskmanager_job_task_operator_numRecordsInPerSecond
    //   flink_taskmanager_job_task_operator_numRecordsOutPerSecond
    //
    // Checkpoint duration:
    //   flink_jobmanager_job_lastCheckpointDuration
    //
    // Checkpoint size:
    //   flink_jobmanager_job_lastCheckpointFullSize
    //
    // Backpressure:
    //   flink_taskmanager_job_task_busyTimeMsPerSecond
    //   flink_taskmanager_job_task_backPressuredTimeMsPerSecond
    //
    // Kafka lag:
    //   flink_taskmanager_job_task_operator_KafkaConsumer
    //     _records_lag_max
}

What does increasing checkpoint duration indicate?

Steadily increasing checkpoint duration usually means state is growing or the state backend is under pressure. If a checkpoint cannot finish before the next one starts, the job can stall.

TEXT
Checkpoint duration trend:

  Time     Duration   State Size    Status
  10:00    2.1s       500 MB        Healthy
  10:05    3.4s       650 MB        Growing
  10:10    5.8s       900 MB        Warning
  10:15    12.1s      1.4 GB        Danger
  10:20    TIMEOUT    1.9 GB        STALL

Root cause: state growing without bound
Fix: add state TTL, enable incremental checkpoints,
     increase managed memory
JAVA
// Prometheus alert for checkpoint duration
// alert: FlinkCheckpointSlowing
// expr: rate(flink_jobmanager_job_lastCheckpointDuration[10m]) > 0.5
// for: 10m
// labels:
//   severity: warning
// annotations:
//   summary: "Checkpoint duration increasing for {{ $labels.job_name }}"

What does growing state size indicate?

Continuously growing state with no plateau signals a state leak. The most common cause is keyed state that is never cleaned up, such as a per-key map or timer that grows without bound.

JAVA
package com.bank.monitoring;

import org.apache.flink.api.common.state.StateTtlConfig;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.time.Time;

public class StateLeakFixDemo {
    // PROBLEM: state grows without bound
    // Per-account MapState with entries never removed
    // State size: 500MB -> 1GB -> 2GB -> 4GB -> OOM

    // FIX 1: State TTL (automatic cleanup)
    public static ValueStateDescriptor<String> ttlState() {
        ValueStateDescriptor<String> desc =
            new ValueStateDescriptor<>("accountData", String.class);
        StateTtlConfig ttl = StateTtlConfig
            .newBuilder(Time.hours(24))
            .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
            .cleanupFullSnapshot()
            .build();
        desc.enableTimeToLive(ttl);
        return desc;
    }

    // FIX 2: Explicit cleanup in onTimer
    // Register cleanup timer when state is created
    // Clear state when timer fires
}

What does backpressure on a specific operator indicate?

Backpressure concentrated on one operator identifies the bottleneck. That operator cannot keep up, so everything upstream slows. The busy operator has high busyTime and zero backPressuredTime.

DIAGRAM
Backpressure diagnosis:

  Operator           Busy%   BackPressured%   Idle%
  ──────────         ─────   ──────────────   ─────
  KafkaSource        12%     83%              5%    <-- waiting
  Enricher           18%     77%              5%    <-- waiting
  FraudScorer        95%     0%               5%    <-- BOTTLENECK
  AlertSink          30%     0%               70%   <-- idle

  FraudScorer is the bottleneck:
    - 95% busy (doing work)
    - 0% backpressured (not waiting on anyone downstream)
    - Everything upstream is backpressured behind it

  Fix: increase FraudScorer parallelism, optimize scoring logic,
       or check for key skew
JAVA
// Grafana query to find the bottleneck operator:
// SELECT operator, avg(busyTimeMsPerSecond)
// FROM flink_task_metrics
// WHERE backPressuredTimeMsPerSecond < 50
//   AND busyTimeMsPerSecond > 900
// This finds operators that are busy but NOT backpressured
// = they are the bottleneck

What does rising Kafka consumer lag indicate?

Rising Kafka lag means the job as a whole is slower than the input rate. This ties back to backpressure: the pipeline cannot consume as fast as the source produces, so unconsumed records accumulate in Kafka.

TEXT
Kafka lag trend:

  Time     Lag (records)   Throughput (rec/s)   Status
  10:00    1,000           10,000               Healthy
  10:05    15,000          8,000                Falling behind
  10:10    50,000          6,000                Growing lag
  10:15    150,000         5,000                Critical

Root cause: throughput < input rate
Fix: find backpressured bottleneck, increase parallelism,
     optimize slow operator, or scale external system
JAVA
// Prometheus alert for Kafka lag
// alert: FlinkKafkaLagHigh
// expr: flink_kafka_consumer_records_lag_max > 100000
// for: 5m
// labels:
//   severity: critical
// annotations:
//   summary: "Kafka lag exceeding 100K for {{ $labels.topic }}"

How do you correlate metrics to find root cause?

The real skill is correlating multiple metrics into a single root-cause story. Backpressure plus rising lag plus a busy operator points directly at the culprit.

TEXT
Correlation example:

  Observation 1: Kafka lag growing
  Observation 2: Checkpoint duration increasing
  Observation 3: FraudScorer 95% busy, 0% backpressured
  Observation 4: State size growing for FraudScorer

  Root cause story:
    FraudScorer has a state leak (state never cleaned up)
    --> state grows
    --> checkpoint size and duration increase
    --> FraudScorer becomes bottleneck (slow state access)
    --> backpressure propagates upstream
    --> source slows, Kafka lag grows

  Fix: add state TTL to FraudScorer
  Result: state plateaus, checkpoints shrink,
          backpressure clears, lag drains

What should a senior engineer know about monitoring?

A senior engineer should alert on trends rather than absolute values, correlate metrics across operators, watch failed-checkpoint and restart counts, and enable latency markers only when investigating because they add overhead.

JAVA
package com.bank.monitoring;

public class SeniorMonitoringChecklist {
    // Senior monitoring checklist for NBI Flink jobs:
    //
    // 1. Alert on checkpoint duration TREND (rate of change)
    //    not just absolute value
    // 2. Alert on state size GROWTH (sustained increase)
    // 3. Alert on Kafka lag exceeding threshold
    // 4. Monitor restart count (frequent restarts = hidden problem)
    // 5. Monitor failed checkpoint count
    // 6. Correlate: backpressure + lag + busy operator = root cause
    // 7. Latency markers: enable only during investigation
    //    (they add overhead in high-throughput jobs)
    // 8. Set up PagerDuty/OpsGenie alerts for critical metrics
    //
    // Grafana dashboard panels:
    //   Row 1: Throughput (records/sec) per operator
    //   Row 2: Checkpoint duration + size
    //   Row 3: Backpressure per operator (busy/backpressured/idle)
    //   Row 4: Kafka consumer lag
    //   Row 5: State size per operator
    //   Row 6: Restart count + failed checkpoints
}

Visual Overview

DIAGRAM
  Flink Monitoring Architecture

  ┌────────────────────────────────────────────────┐
  │                 Flink Cluster                    │
  │                                                  │
  │  JobManager ──▶ Web Dashboard (:8081)            │
  │    │              (job graph, checkpoints,        │
  │    │               backpressure, exceptions)      │
  │    │                                              │
  │  TaskManager-1 ──▶ Prometheus Reporter (:9249)   │
  │  TaskManager-2 ──▶ Prometheus Reporter (:9249)   │
  │  TaskManager-3 ──▶ Prometheus Reporter (:9249)   │
  └────────────────────────┬───────────────────────┘
                           │ scrape
                           ▼
  ┌────────────────────────────────────────────────┐
  │              Prometheus Server                   │
  │  (stores time-series metrics)                    │
  └────────────────────────┬───────────────────────┘
                           │ query
                           ▼
  ┌────────────────────────────────────────────────┐
  │              Grafana Dashboards                  │
  │  ┌──────────┐ ┌──────────┐ ┌───────────────┐   │
  │  │Throughput│ │Checkpoint│ │ Backpressure  │   │
  │  │ rec/sec  │ │ duration │ │ per operator  │   │
  │  └──────────┘ └──────────┘ └───────────────┘   │
  │  ┌──────────┐ ┌──────────┐ ┌───────────────┐   │
  │  │Kafka Lag │ │State Size│ │ Restart Count │   │
  │  └──────────┘ └──────────┘ └───────────────┘   │
  │                                                  │
  │  Alert Rules ──▶ PagerDuty / OpsGenie            │
  └────────────────────────────────────────────────┘


  Trouble Indicators:

  Metric                     Healthy        Trouble
  ──────                     ───────        ───────
  Checkpoint duration        2-5s stable    Growing toward interval
  State size                 Plateau        Continuous growth
  Backpressure               None           Concentrated on 1 operator
  Kafka lag                  < 1000         Growing without bound
  Restart count              0              Increasing

Key Takeaway

Monitoring a Flink job combines the built-in Web Dashboard with the metrics system exported to Prometheus and Grafana. The Web Dashboard is the first stop for live inspection: it shows the job graph, per-operator backpressure, checkpoint history, watermark progress, and recent exceptions. For continuous monitoring, the Prometheus reporter exports metrics that Grafana visualizes with dashboards and alerts.

The real skill is interpreting the metrics. Steadily increasing checkpoint duration means state is growing or the backend is under pressure. Continuously growing state with no plateau signals a state leak. Backpressure concentrated on one operator identifies the bottleneck. Rising Kafka lag means the job is slower than the input rate. The senior insight is to alert on trends and correlate metrics: backpressure plus rising lag plus a busy operator points straight at the culprit.

At the National Bank of India, our real-time fraud job started missing latency targets. Rather than guess, I read the metrics. State size was climbing with no plateau, checkpoint duration was lengthening, the fraud operator's backpressure was rising, and Kafka lag was increasing. Correlating those told a single story: the fraud operator had a state leak that made it the bottleneck. The fix was adding state TTL, after which state plateaued, checkpoints shrank, backpressure cleared, and lag drained.

Being able to name which metric reveals which failure mode, correlate them into a root cause, and alert on trends instead of reacting after a crash is exactly what keeps a production streaming platform healthy.

Remember one sentence: Monitor checkpoint duration, state size, operator backpressure, and Kafka lag, and correlate them to find the root cause before the job crashes.

---

Share
Use ← → arrow keys to navigate