Skip to content

Commit 010df2e

Browse files
authored
fix: reconcile single-node SNSD topology (#202)
1 parent 3b26881 commit 010df2e

2 files changed

Lines changed: 124 additions & 5 deletions

File tree

‎lib/performance-data.ts‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface ClusterDiagnostics {
2929
listingHealth: StatusDiagnostic
3030
workloadAdmission: StatusDiagnostic
3131
peers: PeerHealthDiagnostic[]
32-
membership: Array<{ nodeId: string; gridHost?: string; isLocal?: boolean }>
32+
membership: Array<{ nodeId: string; gridHost?: string; serverInfoEndpoint?: string; isLocal?: boolean }>
3333
topologyDrives: Array<{
3434
nodeId: string
3535
poolIndex?: number
@@ -393,8 +393,18 @@ export function normalizeClusterDiagnostics(value: unknown): ClusterDiagnostics
393393
const nodeId = asSafeText(node.node_id ?? node.nodeId ?? node.NodeId)
394394
if (!nodeId) return []
395395
const gridHost = asSafeText(node.grid_host ?? node.gridHost ?? node.GridHost)
396+
const serverInfoEndpoint = asSafeText(
397+
node.server_info_endpoint ?? node.serverInfoEndpoint ?? node.ServerInfoEndpoint,
398+
)
396399
const isLocal = asBoolean(node.is_local ?? node.isLocal ?? node.IsLocal)
397-
return [{ nodeId, ...(gridHost ? { gridHost } : {}), ...(isLocal !== undefined ? { isLocal } : {}) }]
400+
return [
401+
{
402+
nodeId,
403+
...(gridHost ? { gridHost } : {}),
404+
...(serverInfoEndpoint ? { serverInfoEndpoint } : {}),
405+
...(isLocal !== undefined ? { isLocal } : {}),
406+
},
407+
]
398408
})
399409
const topologyDrives = asArray<unknown>(membershipRecord.drives ?? membershipRecord.Drives).flatMap((value) => {
400410
const drive = asRecord(value)
@@ -527,9 +537,11 @@ function getEndpointIdentity(value: string | undefined) {
527537
}
528538

529539
function getMemberIdentities(member: ClusterDiagnostics["membership"][number]) {
530-
return [getEndpointIdentity(member.nodeId), getEndpointIdentity(member.gridHost)].filter(
531-
(identity): identity is NonNullable<ReturnType<typeof getEndpointIdentity>> => Boolean(identity),
532-
)
540+
return [
541+
getEndpointIdentity(member.serverInfoEndpoint),
542+
getEndpointIdentity(member.nodeId),
543+
getEndpointIdentity(member.gridHost),
544+
].filter((identity): identity is NonNullable<ReturnType<typeof getEndpointIdentity>> => Boolean(identity))
533545
}
534546

535547
function findMatchingMember(server: ServerInfo, diagnostics: ClusterDiagnostics) {
@@ -729,6 +741,14 @@ function reconcileTopologyServers(system: SystemInfo, diagnostics: ClusterDiagno
729741
}
730742
}
731743

744+
if (membership.length === 1 && membership[0].isLocal && reportedServers.length === 1) {
745+
return {
746+
servers: reportedServers,
747+
reportedServers: 1,
748+
expectedServers: 1,
749+
}
750+
}
751+
732752
const usedIndexes = new Set<number>()
733753
let matchedServers = 0
734754
const servers = membership.map((member): ServerInfo => {

‎tests/lib/performance-data.test.js‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,105 @@ test("the #1429 reporter payload uses v4 membership without duplicating v3 rows"
555555
})
556556
})
557557

558+
test("a single local path member reuses the only reported SNSD server", () => {
559+
const system = normalizeSystemInfo({
560+
info: {
561+
backend: { onlineDisks: 1, offlineDisks: 0, unknownDisks: 0, totalDrivesPerSet: [1] },
562+
servers: [
563+
{
564+
endpoint: ":::9000",
565+
state: "online",
566+
uptime: 42,
567+
drives: [{ state: "ok" }],
568+
network: { "::": "online" },
569+
},
570+
],
571+
},
572+
})
573+
const diagnostics = normalizeClusterDiagnostics({
574+
snapshot: {
575+
membership: {
576+
nodes: [{ node_id: "local", grid_host: "", is_local: true }],
577+
drives: [{ pool_index: 0, set_index: 0, disk_index: 0, node_id: "local" }],
578+
},
579+
pool_state: { pools: [{ pool_index: 0, set_count: 1, drives_per_set: 1, endpoint_count: 1 }] },
580+
},
581+
})
582+
583+
const view = buildRunningStatusView(system, diagnostics)
584+
585+
assert.deepEqual(
586+
view.servers?.map((server) => server.endpoint),
587+
[":::9000"],
588+
)
589+
assert.deepEqual(view.serverSummary, {
590+
online: 1,
591+
offline: 0,
592+
degraded: 0,
593+
initializing: 0,
594+
unknown: 0,
595+
})
596+
assert.equal(view.topology.reportedServers, 1)
597+
assert.equal(view.topology.expectedServers, 1)
598+
assert.equal(view.topology.incomplete, false)
599+
})
600+
601+
test("a local server-info identity matches without guessing among multiple members", () => {
602+
const system = normalizeSystemInfo({
603+
info: {
604+
backend: { onlineDisks: 2, offlineDisks: 0, unknownDisks: 0 },
605+
servers: [
606+
{ endpoint: ":::9000", state: "online", drives: [{ state: "ok" }] },
607+
{ endpoint: "node-b:9000", state: "online", drives: [{ state: "ok" }] },
608+
],
609+
},
610+
})
611+
const diagnostics = normalizeClusterDiagnostics({
612+
snapshot: {
613+
membership: {
614+
nodes: [
615+
{ node_id: "local", is_local: true, server_info_endpoint: ":::9000" },
616+
{ node_id: "node-b:9000", grid_host: "http://node-b:9000", is_local: false },
617+
],
618+
},
619+
},
620+
})
621+
622+
const view = buildRunningStatusView(system, diagnostics)
623+
624+
assert.deepEqual(
625+
view.servers?.map((server) => server.endpoint),
626+
[":::9000", "node-b:9000"],
627+
)
628+
assert.equal(view.topology.reportedServers, 2)
629+
assert.equal(view.topology.expectedServers, 2)
630+
assert.equal(view.topology.incomplete, false)
631+
})
632+
633+
test("a local member stays unresolved when multiple reported servers are ambiguous", () => {
634+
const system = normalizeSystemInfo({
635+
info: {
636+
servers: [
637+
{ endpoint: "node-a:9000", state: "online" },
638+
{ endpoint: "node-b:9000", state: "online" },
639+
],
640+
},
641+
})
642+
const diagnostics = normalizeClusterDiagnostics({
643+
snapshot: { membership: { nodes: [{ node_id: "local", is_local: true }] } },
644+
})
645+
646+
const view = buildRunningStatusView(system, diagnostics)
647+
648+
assert.deepEqual(
649+
view.servers?.map((server) => server.endpoint),
650+
["local", "node-a:9000", "node-b:9000"],
651+
)
652+
assert.equal(view.topology.reportedServers, 0)
653+
assert.equal(view.topology.expectedServers, 1)
654+
assert.equal(view.topology.incomplete, true)
655+
})
656+
558657
test("a v3-only incomplete drive denominator is unknown without inventing a server denominator", () => {
559658
const view = buildRunningStatusView(normalizeSystemInfo(reporterV3Payload))
560659

0 commit comments

Comments
 (0)