Guides
Stream Lineage
Visualize producers, topics, processors, and consumers as a graph
Overview
Stream Lineage renders an interactive, left-to-right graph of every producer, topic, processor
(Managed Connect, Flink SQL, Tableflow), lake table, and consumer group running in your
(business_unit, stage) context. It answers the question
"who produces to and consumes from this topic, and what runs in between?" without leaving
the self-service portal.
The graph is reconstructed on demand by querying Confluent Cloud directly — Managed Connect, Flink SQL v1, Tableflow, the Telemetry (Metrics) API, the Schema Registry, and the Kafka REST endpoint — and mapping the results to the open OpenLineage object model. Nothing is stored: every page load rebuilds the graph from authoritative sources (with a short cache window — see Caching).
Two surfaces share the same backend:
-
The full-lineage page at
/ui/lineage— cluster-wide graph for the current context. -
The Stream Lineage card on the topic page
(
/ui/topics/{bu}/{stage}/{topic}) — a 1-hop subgraph centred on the focused topic. It calls the same endpoint with an extra?topic=<name>query param that pushes the topic filter server-side into the Metrics queries, so the response is small and fast.
Reference
Access
Stream Lineage is writer-gated: developers, BU admins, super-admins, and
platform admins can view it. Viewers and unauthenticated users are denied. The gate applies
to both the page (/ui/lineage) and the backing endpoint
(GET /{business_unit}/{stage}/lineage); the sidebar link
is also hidden for viewers so they normally never reach the page.
Why writer-gated?
How It Works
On every uncached page load the backend issues parallel async calls to seven Confluent sources and merges the results into a single OpenLineage event stream. The frontend then bootstraps a Cytoscape.js graph with a dagre layout.
When the request carries ?topic=<name> (the topic-page
card always does), the dataflow source AND-filters every
Metrics query on metric.topic so the API returns only rows
involving that topic. Cluster-wide callers (/ui/lineage) omit
the param and get the full graph.
| Source | Confluent API | Contributes |
|---|---|---|
connect |
Managed Connect REST | Connector name + direction (source/sink) per lcc-* id. Used to reclassify the matching dataflow producer / consumer-group edges into Connect source / Connect sink nodes — the topic bindings themselves come from telemetry, not the static connector config. |
flink |
Flink SQL v1 + Compute Pools (fcpm/v2) | RUNNING Flink statements → topic-to-topic edges (parsed from the SQL) |
tableflow |
Tableflow v1 | Topic → Delta / Iceberg table sync edges |
dataflow |
Telemetry API /v2/metrics/dataflow/query |
Producer & consumer-group edges + per-topic throughput facet. Three queries (bytes_in, bytes_out, consumer_lag_offsets) fired in parallel — matching exactly what the Confluent Cloud portal fires. Surfaces non-committing consumers (e.g. Spark Structured Streaming) the documented consumer_lag_offsets metric never returns. |
dataflow_ksql |
Telemetry API (ksqlDB query metrics, gated on cluster presence) | ksqlDB query → topic edges (skipped silently when no ksqlDB cluster exists) |
schema_registry |
Schema Registry | Per-topic value & key schemas (facet enrichment) |
kafka_rest |
Kafka REST v3 | Topic partition counts & replication factor (facet enrichment) |
Each source runs independently. If one fails (auth, transport, 5xx), the others still contribute — see Partial Responses.
Reading the Graph
Data flows left to right. Two node families render distinctly:
Dataset nodes
Kafka topics (pink), Delta tables and Iceberg tables (indigo). Topic nodes carry partition, replication, and schema facets when the enrichments succeed.
Job nodes
Anything that moves data, colour-coded by kind: producers and consumer groups (teal), Kafka apps (cyan), Connect source connectors (amber), Connect sink connectors (orange), Flink statements (violet), ksqlDB queries (sky), Tableflow syncs (slate). Job nodes carry kind-specific facets (connector class, Flink SQL excerpt, table format, throughput, etc.).
A Kafka app is a producer and a consumer group that share the same client ID — a Kafka Streams application, typically — merged into one node with edges in both directions, the way the Confluent Cloud portal shows it. A consumer group cluster is the opposite case: several consumer groups driven by one shared client ID, collapsed into a single node with a member count; its side panel lists the members and expands each one.
Shared client ID warning
An amber badge on a node, showing a count, means that many client IDs used by
that node also appear under one or more other nodes. That is almost always a
misconfiguration — two distinct applications deployed with the same
client.id — and it blurs the graph: edges and
principals can no longer be attributed to one of them with certainty. Hover the badge for the
IDs involved, or open the node's side panel: the ⚠ Shared client IDs section names
the other nodes. (A consumer-group cluster carries its member count instead.) Fix it by giving
each application its own client ID.
Who is behind a node — the Principal rows
The side panel of a consumer group, a consumer-group cluster member or a Connect connector shows one Principal row per identity its Kafka clients authenticated as — the service account, identity pool or user actually doing the work, which is what you need when a consumer is unknown to you or a connector misbehaves. A Flink statement's panel shows the principal it runs as. Where a consumer group shares a client ID with other nodes (see the warning above), the panel says so under the rows, because some of those principals may belong to the other nodes.
"No data streaming"
When nothing produced or consumed inside the selected period, the canvas shows a No data streaming notice instead of an empty graph. The period defaults are tight (see the toolbar below), so first widen the time window; lineage data is retained for seven days.
Badge nodes (aggregated, currently disabled)
The graph supports collapsing groups of producers / consumer groups that share an identical topic-set into one PRODUCERS · N or CONSUMER GROUPS · N badge — the same rule the Confluent Cloud portal uses. Aggregation is intentionally turned off in the current build while its effect on real graphs is being evaluated; every producer and consumer group renders as an individual node. The toolbar's Expand all groups button is a no-op until aggregation is re-enabled.
Toolbar & Interaction
| Action | Behaviour |
|---|---|
| Click a node | Opens a right side-panel with the details for that kind of node — identity and principals, production / consumption throughput, the topic's schema, the connector's class, the Flink statement's SQL, and so on — plus a link to the resource's own page where one exists. |
| Period selector | Switches the telemetry lookback window (presets from 10 minutes up to 24 hours). The window is sent as ?start=&end= on the lineage fetch and drives the dataflow queries. The selection is not persisted — every page load resets to the surface's default: Last 10 minutes on /ui/lineage, Last hour on the topic card. Tight defaults keep both pages fast on entry; the Metrics API gets slow at long windows. |
| Search | Type-ahead search across node labels. Selecting a result centres and highlights the node. |
| Fit to screen | Re-runs the layout fit so the whole graph is visible in the canvas. |
| Hide internal resources | Hides Confluent-internal producers, consumer groups, and topics (e.g. _confluent-*) so the graph shows only user-owned flows. |
| Refresh | Cache-busts the browser fetch. The server-side 60 s cache still applies; see Caching. |
| Export PNG | Downloads the full graph (not just the viewport) at 2× scale. |
Caching
Lineage responses are cached server-side for 60 seconds via aiocache, keyed by
(business_unit, stage, window.start, window.end, topic).
Repeated loads within the window share a single build — important because a full graph build can
fan out a dozen or more API calls against Confluent. Cluster-wide (topic=None)
and topic-scoped responses occupy independent cache slots so they don't poison each other.
Picking a different period in the toolbar produces a new cache entry; the default key rolls
forward every minute as the window's end is snapped to the current minute.
The browser also gets Cache-Control: private, max-age=60.
Newly created resources (topics, connectors, consumer groups) show up on the next build after the
TTL expires.
Partial Responses
Source failures never return a 5xx. The endpoint always answers 200 OK
with the events it could build, and adds two response headers when one or more sources failed:
X-Lineage-Failed-Sources: dataflow,flink
A yellow banner above the graph enumerates the failed sources so you can tell at a glance whether you are looking at the full picture or a degraded view. The most common causes:
| Failed source | Likely cause | Fix |
|---|---|---|
dataflow / dataflow_ksql |
Admin Cloud API key is missing the MetricsViewer role. | Grant MetricsViewer at org or env scope on the admin service account. |
flink |
Compute pool listing returned 5xx or Flink SQL v1 rejected the credential. | Verify the per-landing-zone flink_<region>_client_key / _secret entries in App Configuration. |
connect |
At least one connector's config could not be parsed. | Check application logs for the offending connector name; the entire source fails on a single parse error by design. |
schema_registry / kafka_rest |
Per-context credentials missing or expired. | Same credentials used by Topics & Schemas pages — verify those work first. |
Flink not configured ≠ failure
Limitations
No ksqlDB or self-managed Connect
EON does not run ksqlDB or self-managed Connect clusters today. Statements / connectors running outside Confluent Managed Connect & Cloud Flink will not appear.
Spark Structured Streaming granularity
Non-committing consumers (e.g. Spark Structured Streaming, which uses
assign() and an external checkpoint store) are
surfaced via the dataflow bytes_out
rows — one consumer-group node per StreamingQuery (queryRunId),
with all Kafka sources in that query merged into a single node. Per-executor breakdown isn't
shown; "Spark application" isn't a Confluent concept.
SQL parsing is regex-based
Complex Flink SQL patterns (CTEs, lateral joins, deeply nested subqueries) may not yield edges. The statement still renders as a job node — only its input / output detection is affected.
Short default lookback
Producer and consumer edges come from the Telemetry API. Each surface defaults to a short
window — Last 10 minutes on /ui/lineage,
Last hour on the topic card — to keep page loads fast. Clients that haven't produced
or committed in that window won't show up; widen the window via the toolbar to look further
back. Direct API calls without ?start=&end= fall
back to the server-side default controlled by
CONFLUENT_METRICS_LOOKBACK_MINUTES.
Topic-level only
Column / field-level lineage is not extracted. Schemas are attached as facets on topic nodes for inspection, but edges are always topic-to-job-to-topic.
Desktop-only
Below 768 px the page shows an "open on desktop" placeholder. Large DAGs with badges are not usable on a phone screen.