Skip to content

Commit df6fd75

Browse files
authored
vis: TopologyView: refactor region rendering and add region labels (#1629)
1 parent 991619a commit df6fd75

File tree

2 files changed

+71
-37
lines changed

2 files changed

+71
-37
lines changed

packages/ott-vis-panel/src/components/views/TopologyView.tsx

+56-36
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ import { useD3Zoom } from "chartutils";
1919
import { dedupeItems } from "aggregate";
2020
import { useEventBus, type BusEvent } from "eventbus";
2121

22-
/**
23-
* The goal of this component is to show a more accurate topology view from the perspective of actual network connections.
24-
*
25-
* There will be 2 types of trees: Balancer Trees and Monolith Trees. The Balancer Trees will show the connections between Balancers and clients, while the Monolith Trees will show the connections between Monoliths and Rooms.
26-
*
27-
* These trees will be grouped by region, and the nodes will be colored by group. Balancer Trees will be on the left, and Monolith Trees will be on the right, with connections between Balancers and Monoliths, and regions being shown by boxing the trees that are in the same region.
28-
*/
29-
3022
interface TopologyViewProps extends TopologyViewStyleProps {
3123
systemState: SystemState;
3224
width: number;
@@ -46,7 +38,7 @@ interface Subtree {
4638
y: number;
4739
}
4840

49-
interface PrebuiltRegion {
41+
interface UnbuiltRegion {
5042
name: string;
5143
balancerTrees: d3.HierarchyNode<TreeNode>[];
5244
monolithTrees: d3.HierarchyNode<TreeNode>[];
@@ -61,6 +53,13 @@ interface Region {
6153

6254
const DEBUG_BOUNDING_BOXES = false;
6355

56+
/**
57+
* The goal of this component is to show a more accurate topology view from the perspective of actual network connections.
58+
*
59+
* There will be 2 types of trees: Balancer Trees and Monolith Trees. The Balancer Trees will show the connections between Balancers and clients, while the Monolith Trees will show the connections between Monoliths and Rooms.
60+
*
61+
* These trees will be grouped by region, and the nodes will be colored by group. Balancer Trees will be on the left, and Monolith Trees will be on the right, with connections between Balancers and Monoliths, and regions being shown by boxing the trees that are in the same region.
62+
*/
6463
export const TopologyView: React.FC<TopologyViewProps> = ({
6564
systemState,
6665
width,
@@ -99,7 +98,7 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
9998

10099
const svg = d3.select(svgRef.current);
101100

102-
const monolithRegions: Map<string, PrebuiltRegion> = new Map();
101+
const monolithRegions: Map<string, UnbuiltRegion> = new Map();
103102
for (const tree of monolithTrees) {
104103
const region = tree.data.region;
105104
if (!monolithRegions.has(region)) {
@@ -128,9 +127,11 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
128127
.linkRadial<any, TreeNode>()
129128
.angle((d: any) => Math.atan2(d.y, d.x) + Math.PI / 2)
130129
.radius((d: any) => Math.sqrt(d.x * d.x + d.y * d.y));
131-
function renderTrees(trees: Subtree[], groupClass: string) {
132-
svg.select(groupClass)
133-
.selectAll(".tree")
130+
function renderTrees(
131+
trees: Subtree[],
132+
base: d3.Selection<d3.BaseType | SVGElement, any, null, any>
133+
) {
134+
base.selectAll(".tree")
134135
.data(trees)
135136
.join(
136137
create => {
@@ -226,7 +227,7 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
226227
});
227228
}
228229

229-
function buildRegion(region: PrebuiltRegion): Region {
230+
function buildRegion(region: UnbuiltRegion): Region {
230231
const monolithSubtrees: Subtree[] = [];
231232
const balancerSubtrees: Subtree[] = [];
232233

@@ -312,12 +313,15 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
312313
return built;
313314
}
314315

315-
function renderRegion(region: Region) {
316+
function renderRegion(
317+
region: Region,
318+
base: d3.Selection<d3.BaseType | SVGElement, any, null, any>
319+
) {
316320
const monolithSubtrees = region.monolithSubtrees;
317321
const balancerSubtrees = region.balancerSubtrees;
318322

319323
if (DEBUG_BOUNDING_BOXES) {
320-
svg.select(".monolith-trees")
324+
base.select(".monolith-trees")
321325
.selectAll("rect")
322326
.data([...monolithSubtrees, ...balancerSubtrees])
323327
.join("rect")
@@ -330,8 +334,8 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
330334
.attr("stroke-width", 1);
331335
}
332336

333-
renderTrees(balancerSubtrees, ".balancer-trees");
334-
renderTrees(monolithSubtrees, ".monolith-trees");
337+
renderTrees(balancerSubtrees, base.select(".balancer-trees"));
338+
renderTrees(monolithSubtrees, base.select(".monolith-trees"));
335339

336340
interface B2M {
337341
source: Subtree;
@@ -349,7 +353,7 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
349353
.link<B2M, Subtree>(d3.curveStep)
350354
.x((d: any) => d.x + d.tree.x)
351355
.y((d: any) => d.y + d.tree.y);
352-
svg.select(".b2m")
356+
base.select(".b2m")
353357
.selectAll(".link")
354358
.data(b2mLinks, (d: any) => d.source?.tree.data.id + d.target?.tree.data.id)
355359
.join(
@@ -368,31 +372,49 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
368372
const monolithBuiltRegions = new Map<string, Region>();
369373
for (const [name, region] of monolithRegions.entries()) {
370374
monolithBuiltRegions.set(name, buildRegion(region));
371-
renderRegion(monolithBuiltRegions.get(name)!);
372375
}
373376

374377
svg.select(".regions")
375378
.selectAll(".region")
376379
.data(monolithBuiltRegions.values(), (d: any) => d.name)
377380
.join(
378-
create =>
379-
create
380-
.append("rect")
381-
.attr("class", "region")
382-
.attr("x", d => d.bbox[0])
383-
.attr("y", d => d.bbox[1])
384-
.attr("width", d => d.bbox[2] - d.bbox[0])
385-
.attr("height", d => d.bbox[3] - d.bbox[1]),
381+
create => {
382+
const group = create.append("g").attr("class", "region");
383+
group.append("rect").attr("class", "region-bbox");
384+
group.append("text").attr("class", "region-name text");
385+
group.append("g").attr("class", "b2m links");
386+
group.append("g").attr("class", "balancer-trees");
387+
group.append("g").attr("class", "monolith-trees");
388+
return group;
389+
},
386390
update => update,
387391
exit => exit.transition(tr).attr("opacity", 0).remove()
388392
)
389393
.attr("data-nodeid", d => d.name)
390394
.transition(tr)
391-
.attr("x", d => d.bbox[0])
392-
.attr("y", d => d.bbox[1])
393-
.attr("width", d => d.bbox[2] - d.bbox[0])
394-
.attr("height", d => d.bbox[3] - d.bbox[1])
395-
.attr("opacity", 1);
395+
.attr("opacity", 1)
396+
.each(function (region) {
397+
const group = d3.select(this);
398+
399+
renderRegion(region, group);
400+
401+
group
402+
.select(".region-bbox")
403+
.datum(region)
404+
.transition(tr)
405+
.attr("x", d => d.bbox[0])
406+
.attr("y", d => d.bbox[1])
407+
.attr("width", d => d.bbox[2] - d.bbox[0])
408+
.attr("height", d => d.bbox[3] - d.bbox[1]);
409+
410+
group
411+
.select(".region-name")
412+
.datum(region)
413+
.text(d => d.name)
414+
.transition(tr)
415+
.attr("x", d => d.bbox[0] + (d.bbox[2] - d.bbox[0]) / 2)
416+
.attr("y", d => d.bbox[1] + 20);
417+
});
396418
}, [
397419
svgRef,
398420
monolithTrees,
@@ -516,10 +538,8 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
516538
ref={svgRef}
517539
>
518540
<g className="chart">
519-
<g className="regions"></g>
541+
<g className="regions" />
520542
<g className="b2m links" />
521-
<g className="balancer-trees">g</g>
522-
<g className="monolith-trees"></g>
523543
</g>
524544
</svg>
525545
);

packages/ott-vis-panel/src/treeutils.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,28 @@ export function buildFullTree(systemState: SystemState): TreeNode {
1616
children: [],
1717
};
1818

19+
const regions = new Map<string, TreeNode>();
20+
1921
for (const balancer of systemState) {
22+
const regionNode = regions.get(balancer.region) ?? {
23+
id: balancer.region,
24+
region: balancer.region,
25+
group: "region",
26+
children: [],
27+
};
28+
regions.set(regionNode.region, regionNode);
29+
2030
const balancerNode: TreeNode = {
2131
id: balancer.id,
2232
region: balancer.region,
2333
group: "balancer",
2434
children: buildMonolithTrees(balancer.monoliths),
2535
};
26-
tree.children.push(balancerNode);
36+
regionNode.children.push(balancerNode);
37+
}
38+
39+
for (const region of regions.values()) {
40+
tree.children.push(region);
2741
}
2842
return tree;
2943
}

0 commit comments

Comments
 (0)