Implementation:Risingwavelabs Risingwave Relation Graph Page
| Property | Value |
|---|---|
| Component | StreamingGraph |
| File | dashboard/pages/relation_graph.tsx
|
| Language | TypeScript/TSX |
| Lines | 224 |
| Type | Next.js Page Component |
| Framework | React, Chakra UI, nuqs (URL query state) |
Overview
relation_graph.tsx is a dashboard page that visualizes the dependency graph between all relations (tables, materialized views, sources, sinks, subscriptions) in the RisingWave catalog, with back-pressure statistics overlaid on edges. It fetches all relations and their dependency maps, builds a DAG of relation edges, periodically polls streaming stats (every 5 seconds) to compute relation-level back-pressure by mapping fragment-level channel stats to relation-level edges. The page supports time-travel controls for viewing historical stats and URL-based relation selection via nuqs. This provides a high-level system topology view showing how data flows between relations and where bottlenecks exist.
Code Reference
Source Location
dashboard/pages/relation_graph.tsx
Signature
// Internal helper function
function buildDependencyAsEdges(
list: Relation[],
relation_deps: Map<number, number[]>
): RelationPoint[]
// Main page component
export default function StreamingGraph(): JSX.Element
Import
// This is a Next.js page component, routed automatically
// Key imports:
import RelationGraph, { boxHeight, boxWidth } from "../components/RelationGraph"
import TimeControls from "../components/TimeControls"
import useFetch from "../lib/api/fetch"
import {
Relation,
getFragmentToRelationMap,
getRelationDependencies,
getRelations,
relationIsStreamingJob,
} from "../lib/api/streaming"
import {
TimeParams,
createStreamingStatsRefresh,
} from "../lib/api/streamingStats"
I/O Contract
Internal State
| State Variable | Type | Description |
|---|---|---|
relationList |
Relation[] |
All relations fetched via useFetch(getRelations)
|
relationDeps |
Map<number, number[]> |
Relation dependency map via useFetch(getRelationDependencies)
|
selectedId |
null | Currently selected relation ID, synced with URL query param ?id=
|
fragmentToRelationMap |
FragmentToRelationMap |
Mapping from fragment IDs to relation IDs |
channelStats |
Map<string, ChannelDeltaStats> |
Fragment-level channel statistics |
relationStats |
{ [key: number]: RelationStats } |
Relation-level aggregated statistics |
timeParams |
TimeParams |
Time-travel parameters (timestamp and offset) |
buildDependencyAsEdges Function
| Parameter | Type | Description |
|---|---|---|
list |
Relation[] |
All relations to include in the graph |
relation_deps |
Map<number, number[]> |
Dependency map (relation ID -> list of dependency IDs) |
| Returns | RelationPoint[] |
Array of graph nodes with id, name, parentIds, order, width, height, and relation properties
|
Only relations that pass relationIsStreamingJob have their dependency edges included. Relations are sorted in reverse order by ID.
Fragment-to-Relation Stats Mapping
The page converts fragment-level channel stats to relation-level stats using useMemo:
const relationChannelStats = useMemo(() => {
if (!fragmentToRelationMap) return new Map()
let mapping = fragmentToRelationMap.fragmentToRelationMap
if (channelStats) {
let map = new Map<string, ChannelDeltaStats>()
for (const [key, stats] of channelStats) {
const [outputFragment, inputFragment] = key.split("_").map(Number)
let input_relation = mapping[inputFragment]
let output_relation = mapping[outputFragment]
if (input_relation && output_relation && input_relation !== output_relation) {
let key = `${output_relation}_${input_relation}`
map.set(key, stats)
}
}
return map
}
}, [channelStats, fragmentToRelationMap])
Data Fetching Strategy
| Data | Method | Frequency |
|---|---|---|
| Relations | useFetch(getRelations) |
Once on mount |
| Relation dependencies | useFetch(getRelationDependencies) |
Once on mount |
| Fragment-to-relation map | useFetch(getFragmentToRelationMap) |
Once on mount |
| Streaming stats | createStreamingStatsRefresh |
Every 5 seconds (INTERVAL_MS) |
Constants
| Constant | Value | Description |
|---|---|---|
SIDEBAR_WIDTH |
"200px" |
Width of the left sidebar |
INTERVAL_MS |
5000 |
Polling interval for streaming stats (5 seconds) |
Usage Examples
Page Layout Structure
// The page renders a two-panel layout:
// Left sidebar:
// - TimeControls component for historical time queries
// - Scrollable list of relation buttons for selection
// Main area:
// - RelationGraph component with dependency edges and stats overlay
<Flex>
<Flex width={SIDEBAR_WIDTH}>
<TimeControls onApply={handleTimeParamsChange} />
{relationList?.map((r) => (
<Button
key={r.id}
colorScheme={selectedId === r.id ? "blue" : "gray"}
onClick={() => setSelectedId(r.id)}
>
{r.name}
</Button>
))}
</Flex>
<Box flex={1}>
<RelationGraph
nodes={relationDependency}
selectedId={selectedId?.toString()}
setSelectedId={(id) => setSelectedId(parseInt(id))}
channelStats={relationChannelStats}
relationStats={relationStats}
/>
</Box>
</Flex>
Time Controls Integration
const handleTimeParamsChange = (timestamp?: number, offset?: number) => {
setTimeParams({
at: timestamp,
timeOffset: offset,
})
}
// timeParams change triggers the useEffect to recreate the refresh function
// with updated time parameters, enabling historical stats queries
URL Query State
import { parseAsInteger, useQueryState } from "nuqs"
// Selected relation ID is persisted in the URL as ?id=123
const [selectedId, setSelectedId] = useQueryState("id", parseAsInteger)
Related Pages
- Risingwavelabs_Risingwave_Streaming_API_Functions - Provides
getRelations,getRelationDependencies,getFragmentToRelationMap, andrelationIsStreamingJob - Risingwavelabs_Risingwave_StreamingStats_Callbacks - Creates the streaming stats refresh function for periodic polling
- Risingwavelabs_Risingwave_TimeControls - UI component for time-travel parameter input
- Risingwavelabs_Risingwave_TimeUtils - Underlying time parsing functions
- Risingwavelabs_Risingwave_UseFetch_Hook - Used for initial data loading of relations and dependencies
- Risingwavelabs_Risingwave_BackPressure_Utils - Visualization utilities for coloring edges by back-pressure rate
- Risingwavelabs_Risingwave_FragmentDependencyGraph - Related component for fragment-level dependency visualization
- Risingwavelabs_Risingwave_Dashboard_API_Client - The underlying HTTP client for all API calls