Skip to content
File

Blob: ui/operator/Overview.svelte

5.1 KB
1<script lang="ts">
2 import Table from './Table.svelte';
3 import SnapshotTime from './SnapshotTime.svelte';
4 import type { Overview } from './types';
5 
6 let { data }: { data: Overview } = $props();
7 
8 function stateClass(state: string) {
9 if (state === 'Active') return 'border-success/40 text-success';
10 if (['Joining', 'Transferring', 'Leaving'].includes(state)) {
11 return 'border-warning/40 text-warning';
12 }
13 return 'border-zinc-500 text-foreground';
14 }
15</script>
16 
17<p class="text-muted">Local snapshot · <SnapshotTime value={data.observed_at} /></p>
18 
19<dl class="mt-4 mb-5 grid gap-3 border-y border-line py-3 sm:grid-cols-[2fr_1fr_1fr] sm:gap-5">
20 <div class="min-w-0">
21 <dt class="mb-0.5 text-xs text-muted">Physical server</dt>
22 <dd class="font-mono text-[0.92em] wrap-anywhere">{data.identity.address}</dd>
23 </div>
24 <div class="min-w-0">
25 <dt class="mb-0.5 text-xs text-muted">Root virtual node</dt>
26 <dd class="font-mono text-[0.92em] wrap-anywhere">{data.identity.id}</dd>
27 </div>
28 <div class="min-w-0">
29 <dt class="mb-0.5 text-xs text-muted">Storage provider</dt>
30 <dd>{data.provider}</dd>
31 </div>
32</dl>
33 
34<div class="mt-5 mb-2.5 flex flex-wrap items-baseline gap-x-4 gap-y-1">
35 <h2 class="text-lg font-semibold">Virtual nodes ({data.vnodes.length})</h2>
36 <span class="text-[13px] text-muted">Counters since process start</span>
37</div>
38<Table label="Virtual node counters" tableClass="min-w-[60rem]">
39 <thead>
40 <tr>
41 <th scope="col">Node</th>
42 <th scope="col">Membership</th>
43 <th scope="col">Last stabilization</th>
44 <th scope="col" class="text-right">Chord requests</th>
45 <th scope="col" class="text-right">KV requests</th>
46 <th scope="col" class="text-right">RPC errors</th>
47 <th scope="col" class="text-right">Stale KV</th>
48 </tr>
49 </thead>
50 <tbody>
51 {#each data.vnodes as node (node.identity.id)}
52 <tr>
53 <th scope="row">
54 <code class="text-[0.92em]">{node.identity.id}</code>
55 {#if node.root}<span class="block text-xs text-muted">Root</span>{/if}
56 </th>
57 <td><span class="inline-block rounded border px-1.5 py-px text-[13px] whitespace-nowrap {stateClass(node.state)}">{node.state}</span></td>
58 <td>
59 {#if node.last_stabilized}
60 <span class="font-mono text-[0.92em]"><SnapshotTime value={node.last_stabilized} /></span>
61 {:else}
62 <span class="text-muted">Not observed</span>
63 {/if}
64 </td>
65 <td class="text-right">{node.chord_requests.toLocaleString()}</td>
66 <td class="text-right">{node.kv_requests.toLocaleString()}</td>
67 <td class="text-right" class:text-warning={node.rpc_errors > 0}>{node.rpc_errors.toLocaleString()}</td>
68 <td class="text-right" class:text-warning={node.kv_stale > 0}>{node.kv_stale.toLocaleString()}</td>
69 </tr>
70 {:else}
71 <tr><td colspan="7" class="text-muted">No virtual nodes.</td></tr>
72 {/each}
73 </tbody>
74</Table>
75 
76<h2 class="mt-5 mb-2.5 text-lg font-semibold">Known neighbors</h2>
77<Table label="Known neighbors">
78 <thead><tr><th scope="col">Local node</th><th scope="col">Predecessor</th><th scope="col">Successors</th></tr></thead>
79 <tbody>
80 {#each data.vnodes as node (node.identity.id)}
81 <tr>
82 <th scope="row"><code class="text-[0.92em]">{node.identity.id}</code></th>
83 <td>
84 {#if !node.predecessor_available}
85 <span class="text-warning">Unavailable</span>
86 {:else if node.predecessor}
87 <div class="min-w-48">
88 <code class="text-[0.92em]">{node.predecessor.id}</code>
89 <small class="block text-muted">{node.predecessor.address}</small>
90 </div>
91 {:else}
92 <span class="text-muted">Not observed</span>
93 {/if}
94 </td>
95 <td>
96 {#if !node.successors_available}
97 <span class="text-warning">Unavailable</span>
98 {:else}
99 {#each node.successors as peer}
100 <div class="mb-1.5 min-w-48 last:mb-0">
101 <code class="text-[0.92em]">{peer.id}</code>
102 <small class="block text-muted">{peer.address}</small>
103 </div>
104 {:else}
105 <span class="text-muted">Not observed</span>
106 {/each}
107 {/if}
108 </td>
109 </tr>
110 {:else}
111 <tr><td colspan="3" class="text-muted">No neighbors.</td></tr>
112 {/each}
113 </tbody>
114</Table>
115 
116<h2 class="mt-5 mb-2 text-lg font-semibold">Diagnostics</h2>
117<div class="flex flex-wrap items-center gap-x-6 gap-y-1">
118 <span><a class="inline-flex min-h-10 items-center text-accent-300 underline underline-offset-4 hover:text-accent-100" href="/_internal/chord/stats">Ring statistics</a> <small class="text-muted">Scans keys</small></span>
119 <span><a class="inline-flex min-h-10 items-center text-accent-300 underline underline-offset-4 hover:text-accent-100" href="/_internal/chord/graph">Ring graph (DOT)</a> <small class="text-muted">Queries ring</small></span>
120 <a class="inline-flex min-h-10 items-center text-accent-300 underline underline-offset-4 hover:text-accent-100" href="/_internal/debug/pprof/">Runtime profiles</a>
121</div>