querysub 0.616.0 → 0.619.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/-a-archives/archives2.ts +6 -6
- package/src/-f-node-discovery/NodeDiscovery.ts +2 -2
- package/src/0-path-value-core/PathValueController.ts +4 -0
- package/src/0-path-value-core/PathValueStats.ts +83 -0
- package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +0 -5
- package/src/1-path-client/RemoteWatcher.ts +4 -2
- package/src/deployManager/MachinesPage.tsx +3 -0
- package/src/deployManager/urlParams.ts +1 -1
- package/src/diagnostics/managementPages.tsx +4 -6
- package/src/diagnostics/misc-pages/FunctionRunnersSection.tsx +38 -0
- package/src/diagnostics/misc-pages/LatencyGraphSection.tsx +524 -0
- package/src/diagnostics/misc-pages/PathValueServersSection.tsx +154 -0
- package/src/diagnostics/misc-pages/RoutingTablePage.tsx +34 -597
- package/src/diagnostics/misc-pages/SnapshotViewer.tsx +1 -2
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
module.allowclient = true;
|
|
2
|
+
|
|
3
|
+
import { qreact } from "../../4-dom/qreact";
|
|
4
|
+
import { css } from "typesafecss";
|
|
5
|
+
import { getBrowserUrlNode } from "../../-f-node-discovery/NodeDiscovery";
|
|
6
|
+
import { sort } from "socket-function/src/misc";
|
|
7
|
+
import { formatNumber } from "socket-function/src/formatting/format";
|
|
8
|
+
import { PATHVALUE_COLOR } from "../../misc/nodeCategoryColors";
|
|
9
|
+
import { Table } from "../../5-diagnostics/Table";
|
|
10
|
+
import type { SummaryEntry } from "sliftutils/treeSummary";
|
|
11
|
+
import { PathValueDirection, PathValueSummaryState } from "../../0-path-value-core/PathValueStats";
|
|
12
|
+
import { AuthorityRangeBar, ID_CHARS, NodeAuthorityInfo, RoutingTableSynced, machineIdOf, threadLabel } from "./RoutingTablePage";
|
|
13
|
+
|
|
14
|
+
const PATH_SUMMARY_LIMIT = 100;
|
|
15
|
+
const MACHINE_CELL_WIDTH_CH = ID_CHARS + 4;
|
|
16
|
+
|
|
17
|
+
// One direction's top-N path breakdown for a server, heaviest first. Only mounted when a server row is expanded, so
|
|
18
|
+
// the (heavy) tree is only ever fetched on demand.
|
|
19
|
+
class PathSummaryTable extends qreact.Component<{ nodeId: string; direction: PathValueDirection }> {
|
|
20
|
+
render() {
|
|
21
|
+
let { nodeId, direction } = this.props;
|
|
22
|
+
let summaries: SummaryEntry<PathValueSummaryState>[] | undefined;
|
|
23
|
+
try {
|
|
24
|
+
summaries = RoutingTableSynced(getBrowserUrlNode()).getNodePathSummary(nodeId, direction, PATH_SUMMARY_LIMIT);
|
|
25
|
+
} catch (e: any) {
|
|
26
|
+
return <div className={css.colorhsl(0, 60, 40)}>{e.stack ?? String(e)}</div>;
|
|
27
|
+
}
|
|
28
|
+
if (!summaries) return <div className={css.colorhsl(0, 0, 50)}>Loading...</div>;
|
|
29
|
+
if (!summaries.length) return <div className={css.colorhsl(0, 0, 50)}>(none)</div>;
|
|
30
|
+
// getSummaries returns path-sorted; we want the heaviest paths first.
|
|
31
|
+
sort(summaries, entry => -entry.weight);
|
|
32
|
+
let rows = summaries.map(entry => ({
|
|
33
|
+
path: entry.path,
|
|
34
|
+
count: formatNumber(entry.weight),
|
|
35
|
+
}));
|
|
36
|
+
return <Table
|
|
37
|
+
rows={rows}
|
|
38
|
+
columns={{
|
|
39
|
+
path: { title: "Path" },
|
|
40
|
+
count: { title: "Count" },
|
|
41
|
+
}}
|
|
42
|
+
/>;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// One row per path-value server, grouped by machine. Reads/writes lead the line; expanding reveals that server's
|
|
47
|
+
// per-path breakdown for both directions.
|
|
48
|
+
class AuthorityNodeRow extends qreact.Component<{ info: NodeAuthorityInfo }> {
|
|
49
|
+
state = {
|
|
50
|
+
expanded: false,
|
|
51
|
+
};
|
|
52
|
+
render() {
|
|
53
|
+
let info = this.props.info;
|
|
54
|
+
let spec = info.spec;
|
|
55
|
+
let expanded = this.state.expanded;
|
|
56
|
+
let totals = RoutingTableSynced(getBrowserUrlNode()).getNodePathTotals(info.nodeId);
|
|
57
|
+
let machineId = machineIdOf(info.nodeId);
|
|
58
|
+
return <div
|
|
59
|
+
className={css.button.vbox(6).pad2(10).fillWidth.hsl(0, 0, 99).borderTop("1px solid hsl(0, 0%, 88%)")}
|
|
60
|
+
onClick={() => this.state.expanded = !expanded}
|
|
61
|
+
>
|
|
62
|
+
<div className={css.hbox(10).fillWidth}>
|
|
63
|
+
<span>{expanded ? "▼" : "▶"}</span>
|
|
64
|
+
<span className={css.width(`${MACHINE_CELL_WIDTH_CH}ch`).flexShrink0.color(PATHVALUE_COLOR)} title="path values received / sent">
|
|
65
|
+
↓{totals ? formatNumber(totals.received) : "…"} ↑{totals ? formatNumber(totals.sent) : "…"}
|
|
66
|
+
</span>
|
|
67
|
+
<span className={css.width(`${MACHINE_CELL_WIDTH_CH}ch`).flexShrink0.colorhsl(0, 0, 45)} title={machineId}>
|
|
68
|
+
{machineId.slice(0, ID_CHARS)}
|
|
69
|
+
</span>
|
|
70
|
+
<span className={css.boldStyle} title={info.nodeId}>{threadLabel(info.nodeId)}</span>
|
|
71
|
+
{spec && <span className={css.colorhsl(0, 0, 45)}>{spec.routeStart.toFixed(4)} - {spec.routeEnd.toFixed(4)}</span>}
|
|
72
|
+
{spec && <AuthorityRangeBar start={spec.routeStart} end={spec.routeEnd} />}
|
|
73
|
+
{spec?.excludeDefault && <span className={css.colorhsl(0, 70, 35)}>(excludes default)</span>}
|
|
74
|
+
</div>
|
|
75
|
+
{expanded &&
|
|
76
|
+
<div className={css.hbox(16).wrap.fillWidth.alignItems("flex-start")} onClick={e => e.stopPropagation()}>
|
|
77
|
+
<div className={css.vbox(4)}>
|
|
78
|
+
<div className={css.boldStyle.color(PATHVALUE_COLOR)}>Received (↓) — top {PATH_SUMMARY_LIMIT} paths</div>
|
|
79
|
+
<PathSummaryTable nodeId={info.nodeId} direction="received" />
|
|
80
|
+
</div>
|
|
81
|
+
<div className={css.vbox(4)}>
|
|
82
|
+
<div className={css.boldStyle.color(PATHVALUE_COLOR)}>Sent (↑) — top {PATH_SUMMARY_LIMIT} paths</div>
|
|
83
|
+
<PathSummaryTable nodeId={info.nodeId} direction="sent" />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
}
|
|
87
|
+
</div>;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Invalidate: drop every cached synced call for this node so the whole page's data disappears and refetches.
|
|
92
|
+
function invalidateRoutingTable(): void {
|
|
93
|
+
let synced = RoutingTableSynced(getBrowserUrlNode());
|
|
94
|
+
synced.getNodePathTotals.resetAll();
|
|
95
|
+
synced.getNodePathSummary.resetAll();
|
|
96
|
+
}
|
|
97
|
+
// Clear: wipe the in-memory path-value stats (totals and trees) on every node, then drop only the stats caches so the
|
|
98
|
+
// now-empty stats reload — leaving the specs/latency/traffic caches untouched.
|
|
99
|
+
async function clearAllPathStats(nodeIds: string[]): Promise<void> {
|
|
100
|
+
let synced = RoutingTableSynced(getBrowserUrlNode());
|
|
101
|
+
await Promise.all(nodeIds.map(nodeId => synced.clearNodePathStats.promise(nodeId).catch(() => { })));
|
|
102
|
+
synced.getNodePathTotals.resetAll();
|
|
103
|
+
synced.getNodePathSummary.resetAll();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// A list of server rows, most-active first (by total path values sent + received).
|
|
107
|
+
class PathServerList extends qreact.Component<{ infos: NodeAuthorityInfo[]; emptyText: string }> {
|
|
108
|
+
render() {
|
|
109
|
+
let synced = RoutingTableSynced(getBrowserUrlNode());
|
|
110
|
+
let sumOf = (nodeId: string) => {
|
|
111
|
+
let totals = synced.getNodePathTotals(nodeId);
|
|
112
|
+
return (totals?.sent ?? 0) + (totals?.received ?? 0);
|
|
113
|
+
};
|
|
114
|
+
let infos = this.props.infos.slice();
|
|
115
|
+
// Two stable passes: machine/thread as a tiebreak, then sent+received descending as the primary order (so rows
|
|
116
|
+
// with equal — or still-loading, hence 0 — totals keep a stable machine/thread order instead of jittering).
|
|
117
|
+
let sep = String.fromCharCode(1);
|
|
118
|
+
sort(infos, x => `${machineIdOf(x.nodeId)}${sep}${threadLabel(x.nodeId)}`);
|
|
119
|
+
sort(infos, x => -sumOf(x.nodeId));
|
|
120
|
+
if (!infos.length) return <div className={css.colorhsl(0, 0, 50)}>{this.props.emptyText}</div>;
|
|
121
|
+
return <div className={css.vbox(0).fillWidth.bord2(0, 0, 88)}>
|
|
122
|
+
{infos.map(info => <AuthorityNodeRow key={info.nodeId} info={info} />)}
|
|
123
|
+
</div>;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export class PathValueServersSection extends qreact.Component<{ infos: NodeAuthorityInfo[] }> {
|
|
128
|
+
render() {
|
|
129
|
+
let infos = this.props.infos;
|
|
130
|
+
let allNodeIds = infos.map(x => x.nodeId);
|
|
131
|
+
// Path value servers are the authorities (they own a route range). Every other node can still send/receive
|
|
132
|
+
// path values, so it gets its own list below.
|
|
133
|
+
let serverInfos = infos.filter(x => x.spec && x.spec.routeStart >= 0 && x.spec.routeEnd >= 0);
|
|
134
|
+
let otherInfos = infos.filter(x => !(x.spec && x.spec.routeStart >= 0 && x.spec.routeEnd >= 0));
|
|
135
|
+
|
|
136
|
+
let buttonClass = css.pad2(12, 8).button.bord2(0, 0, 20).hsl(0, 0, 100).whiteSpace("nowrap");
|
|
137
|
+
return <div className={css.vbox(8).fillWidth}>
|
|
138
|
+
<div className={css.hbox(12).alignItems("center")}>
|
|
139
|
+
<h2 className={css.margin(0)}>Path Value Servers ({serverInfos.length})</h2>
|
|
140
|
+
<button className={buttonClass} onClick={() => invalidateRoutingTable()}>Invalidate</button>
|
|
141
|
+
<button className={buttonClass} onClick={() => void clearAllPathStats(allNodeIds)}>Clear stats</button>
|
|
142
|
+
</div>
|
|
143
|
+
<div className={css.fontSize(13).colorhsl(0, 0, 45)}>
|
|
144
|
+
Path values each server has received (↓) and sent (↑) since startup or the last clear. Expand a server for its top {PATH_SUMMARY_LIMIT} paths in each direction.
|
|
145
|
+
</div>
|
|
146
|
+
<PathServerList infos={serverInfos} emptyText="(no path value servers)" />
|
|
147
|
+
<h2 className={css.margin(0)}>Other Senders ({otherInfos.length})</h2>
|
|
148
|
+
<div className={css.fontSize(13).colorhsl(0, 0, 45)}>
|
|
149
|
+
Nodes that aren't path value servers but still send/receive path values.
|
|
150
|
+
</div>
|
|
151
|
+
<PathServerList infos={otherInfos} emptyText="(no other senders)" />
|
|
152
|
+
</div>;
|
|
153
|
+
}
|
|
154
|
+
}
|