Implementation:Risingwavelabs Risingwave FragmentGraph Component
| Property | Value |
|---|---|
| Component | FragmentGraph |
| Source File | dashboard/components/FragmentGraph.tsx
|
| Language | TypeScript / TSX |
| Lines | 610 |
| Category | Dashboard UI Component |
| Framework | React, D3.js, dagre |
Overview
The FragmentGraph component is the most complex visualization in the RisingWave dashboard. It renders an interactive SVG graph that displays streaming plan nodes organized within fragment bounding boxes, connected by inter-fragment edges. The visualization uses a two-layer layout approach: D3 hierarchy tree layout for positioning plan nodes within each fragment, and dagre (a directed graph layout library) for positioning fragment boxes relative to each other.
Fragment bounding boxes are color-coded based on epoch latency, edges are colored and sized based on backpressure rates, and individual stream operator nodes are rendered as clickable circles that open a detail modal with full JSON plan node data.
Code Reference
Source Location
dashboard/components/FragmentGraph.tsx
Full URL: dashboard/components/FragmentGraph.tsx
Signature
export default function FragmentGraph({
planNodeDependencies,
fragmentDependency,
selectedFragmentId,
channelStats,
fragmentStats,
}: {
planNodeDependencies: Map<string, d3.HierarchyNode<PlanNodeDatum>>
fragmentDependency: FragmentBox[]
selectedFragmentId?: string
channelStats?: Map<string, ChannelDeltaStats>
fragmentStats?: { [fragmentId: number]: FragmentStats }
}): JSX.Element
Imports
import {
Button, Modal, ModalBody, ModalCloseButton, ModalContent,
ModalFooter, ModalHeader, ModalOverlay, theme, useDisclosure,
} from "@chakra-ui/react"
import loadable from "@loadable/component"
import * as d3 from "d3"
import * as dagre from "dagre"
import { cloneDeep } from "lodash"
import { Fragment, useCallback, useEffect, useRef, useState } from "react"
import { Edge, Enter, FragmentBox, Position } from "../lib/layout"
import { PlanNodeDatum } from "../pages/fragment_graph"
import { ChannelDeltaStats, FragmentStats } from "../proto/gen/monitor_service"
import { StreamNode } from "../proto/gen/stream_plan"
import {
backPressureColor, backPressureWidth, epochToUnixMillis, latencyToColor,
} from "./utils/backPressure"
I/O Contract
Inputs
| Parameter | Type | Required | Description |
|---|---|---|---|
planNodeDependencies |
Map<string, d3.HierarchyNode<PlanNodeDatum>> |
Yes | Map from fragment ID to d3 hierarchy tree of plan nodes within that fragment |
fragmentDependency |
FragmentBox[] |
Yes | Array of fragment boxes describing the DAG of fragment-to-fragment dependencies |
selectedFragmentId |
string |
No | The currently selected fragment ID for visual highlighting |
channelStats |
Map<string, ChannelDeltaStats> |
No | Backpressure and throughput statistics for channels between fragments, keyed by "sourceId_targetId"
|
fragmentStats |
{ [fragmentId: number]: FragmentStats } |
No | Per-fragment stats including current epoch for latency-based coloring |
Outputs
The component renders an SVG element containing:
- Fragment bounding boxes -- rectangles colored by latency (white when stats unavailable, colored by epoch delay otherwise)
- Plan node circles -- clickable circles for each stream operator node within a fragment
- Inter-fragment edges -- lines colored and sized by backpressure rate, with hover tooltips showing throughput and backpressure data
- Plan node detail modal -- a Chakra UI modal displaying the full JSON of the selected stream node via
react-json-view - Fragment labels -- text labels showing fragment IDs and actor IDs
Internal Types
type FragmentLayout = {
id: string
layoutRoot: d3.HierarchyPointNode<PlanNodeDatum>
width: number
height: number
actorIds: string[]
} & Position
Usage Examples
Typical usage from the Fragment Graph page
import FragmentGraph from "../components/FragmentGraph"
<FragmentGraph
planNodeDependencies={planNodeDeps}
fragmentDependency={fragmentBoxes}
selectedFragmentId={selectedId}
channelStats={channelStatsMap}
fragmentStats={fragmentStatsMap}
/>Internal Helpers
treeLayoutFlip
function treeLayoutFlip<Datum>(
root: d3.HierarchyNode<Datum>,
{ dx, dy }: { dx: number; dy: number }
): d3.HierarchyPointNode<Datum>
Applies a D3 tree layout to the hierarchy and then flips x/y coordinates to produce a left-to-right (horizontal) tree rather than the default top-to-bottom layout. Also mirrors the x-axis (LTR to RTL).
boundBox
function boundBox<Datum>(
root: d3.HierarchyPointNode<Datum>,
{ margin }: { margin: { top: number; bottom: number; left: number; right: number } }
): { width: number; height: number }
Computes the bounding box dimensions for a laid-out tree, applying margins and normalizing node positions so the minimum x and y are zero.
Layout Constants
const nodeRadius = 12
const nodeMarginX = nodeRadius * 6 // 72px
const nodeMarginY = nodeRadius * 4 // 48px
const fragmentMarginX = nodeRadius * 2 // 24px
const fragmentMarginY = nodeRadius * 2 // 24px
const fragmentDistanceX = nodeRadius * 5 // 60px
const fragmentDistanceY = nodeRadius * 4 // 48px
Related Pages
- Risingwavelabs_Risingwave_Fragment_Graph_Page -- The parent page that provides data to this component
- Risingwavelabs_Risingwave_RelationGraph_Component -- Sibling graph visualization for relation-level dependencies
- Risingwavelabs_Risingwave_Relations_Component -- Table-based relation list component
- Risingwavelabs_Risingwave_Dashboard_Streaming_API -- API layer used to fetch streaming job data