refactor: clarify running status diagnostics (#180)

This commit is contained in:
cxymds
2026-07-25 22:04:45 +08:00
committed by GitHub
parent b462a20ac0
commit 949c2a884e
21 changed files with 537 additions and 148 deletions
@@ -2,7 +2,53 @@
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card"
import type { RunningStatusTopology } from "@/lib/performance-data"
import type { RunningStatusTopology, StatusDiagnostic } from "@/lib/performance-data"
export type InfrastructureHealthState = "healthy" | "attention" | "unknown"
export function getInfrastructureHealthState({
onlineServers,
offlineServers,
degradedServers,
initializingServers,
unknownServers,
onlineDisks,
offlineDisks,
unknownDisks,
topology,
peerHealth,
storageReadiness,
}: {
onlineServers?: number
offlineServers?: number
degradedServers?: number
initializingServers?: number
unknownServers?: number
onlineDisks?: number
offlineDisks?: number
unknownDisks?: number
topology: RunningStatusTopology
peerHealth?: StatusDiagnostic
storageReadiness?: StatusDiagnostic
}): InfrastructureHealthState {
const resourceNeedsAttention = Boolean((offlineServers ?? 0) + (degradedServers ?? 0) + (offlineDisks ?? 0))
const diagnosticNeedsAttention = [peerHealth, storageReadiness].some((diagnostic) => diagnostic?.state === "degraded")
if (resourceNeedsAttention || diagnosticNeedsAttention) return "attention"
const resourceUncertain = Boolean((initializingServers ?? 0) + (unknownServers ?? 0) + (unknownDisks ?? 0))
const diagnosticUncertain = [peerHealth, storageReadiness].some(
(diagnostic) => diagnostic?.state === "stale" || diagnostic?.state === "unknown",
)
const serverCounts = [onlineServers, offlineServers, degradedServers, initializingServers, unknownServers]
const diskCounts = [onlineDisks, offlineDisks, unknownDisks]
const dataUnavailable =
serverCounts.some((value) => value === undefined) ||
diskCounts.some((value) => value === undefined) ||
serverCounts.reduce<number>((total, value) => total + (value ?? 0), 0) === 0 ||
diskCounts.reduce<number>((total, value) => total + (value ?? 0), 0) === 0
return resourceUncertain || diagnosticUncertain || dataUnavailable || topology.incomplete ? "unknown" : "healthy"
}
function HealthMetric({ label, value, unavailableLabel }: { label: string; value?: number; unavailableLabel: string }) {
return (
@@ -23,6 +69,8 @@ export function PerformanceInfrastructureCard({
offlineDisks,
unknownDisks,
topology,
peerHealth,
storageReadiness,
t,
}: {
onlineServers?: number
@@ -34,30 +82,34 @@ export function PerformanceInfrastructureCard({
offlineDisks?: number
unknownDisks?: number
topology: RunningStatusTopology
peerHealth?: StatusDiagnostic
storageReadiness?: StatusDiagnostic
t: (key: string) => string
}) {
const needsAttention = Boolean((offlineServers ?? 0) + (degradedServers ?? 0) + (offlineDisks ?? 0))
const hasUnknownState = Boolean((initializingServers ?? 0) + (unknownServers ?? 0) + (unknownDisks ?? 0))
const serverCounts = [onlineServers, offlineServers, degradedServers, initializingServers, unknownServers]
const diskCounts = [onlineDisks, offlineDisks, unknownDisks]
const dataUnavailable =
serverCounts.some((value) => value === undefined) ||
diskCounts.some((value) => value === undefined) ||
serverCounts.reduce<number>((total, value) => total + (value ?? 0), 0) === 0 ||
diskCounts.reduce<number>((total, value) => total + (value ?? 0), 0) === 0
const hasIncompleteTopology = topology.incomplete
const status = needsAttention
? t("Needs attention")
: hasUnknownState || dataUnavailable || hasIncompleteTopology
? t("Unknown")
: t("Healthy")
const description = needsAttention
? t("Offline or degraded resources require attention.")
: hasIncompleteTopology
? t("The reported health rows do not cover the full cluster topology.")
: hasUnknownState || dataUnavailable
? t("Some resource health data is unavailable or still initializing.")
: t("All reported servers and disks are online.")
const healthState = getInfrastructureHealthState({
onlineServers,
offlineServers,
degradedServers,
initializingServers,
unknownServers,
onlineDisks,
offlineDisks,
unknownDisks,
topology,
peerHealth,
storageReadiness,
})
const status =
healthState === "attention" ? t("Needs attention") : healthState === "unknown" ? t("Unknown") : t("Healthy")
const description =
healthState === "attention"
? t("Offline or degraded resources require attention.")
: hasIncompleteTopology
? t("The reported health rows do not cover the full cluster topology.")
: healthState === "unknown"
? t("Some resource health data is unavailable or still initializing.")
: t("All reported servers and disks are online.")
return (
<Card className="h-full shadow-none">
@@ -70,13 +122,7 @@ export function PerformanceInfrastructureCard({
<CardDescription>{description}</CardDescription>
</div>
<Badge
variant={
needsAttention
? "destructive"
: hasUnknownState || dataUnavailable || hasIncompleteTopology
? "outline"
: "secondary"
}
variant={healthState === "attention" ? "destructive" : healthState === "unknown" ? "outline" : "secondary"}
aria-live="polite"
>
{status}
@@ -1,8 +1,11 @@
"use client"
import * as React from "react"
import { RiArrowDownSLine } from "@remixicon/react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import { Separator } from "@/components/ui/separator"
import type { ClusterDiagnostics, OperationalStatus, StatusDiagnostic } from "@/lib/performance-data"
@@ -62,71 +65,194 @@ function DiagnosticRow({
scope?.set ? `${t("Set")}: ${scope.set}` : undefined,
scope?.timeout ? `${t("Timeout")}: ${scope.timeout}` : undefined,
].filter(Boolean)
const hasSupportingDetail = Boolean(
diagnostic.lastSuccessfulUpdate ||
showLastSuccessfulUpdate ||
diagnostic.lastError ||
scopeParts.length ||
diagnostic.source ||
diagnostic.historicalStallTimeouts !== undefined ||
diagnostic.hint ||
troubleshooting ||
troubleshootingHref,
)
return (
<div className="grid gap-2 py-4 sm:grid-cols-[minmax(0,12rem)_minmax(0,1fr)] sm:gap-6">
<dt className="flex min-w-0 items-center justify-between gap-3 sm:flex-col sm:items-start sm:justify-start">
<span className="font-medium text-foreground">{label}</span>
<div className="grid gap-2 py-3 sm:grid-cols-[minmax(0,12rem)_minmax(0,7rem)_minmax(0,1fr)] sm:items-start sm:gap-4">
<dt className="min-w-0 font-medium text-foreground">{label}</dt>
<dd>
<Badge variant={getStatusVariant(diagnostic.state)}>{getStatusLabel(diagnostic.state, t)}</Badge>
</dt>
<dd className="flex min-w-0 flex-col gap-2 text-muted-foreground">
</dd>
<dd className="flex min-w-0 flex-col gap-1.5 text-muted-foreground">
<p className="break-words text-foreground [overflow-wrap:anywhere]">
{diagnostic.reason ?? getDefaultDescription(diagnostic.state, t)}
</p>
{diagnostic.lastSuccessfulUpdate || showLastSuccessfulUpdate ? (
<p className="text-xs">
{t("Last successful update")}:{" "}
{diagnostic.lastSuccessfulUpdate ? formatTimestamp(diagnostic.lastSuccessfulUpdate, locale) : t("Unknown")}
</p>
) : null}
{diagnostic.lastError ? (
<p className="break-words text-xs [overflow-wrap:anywhere]">
{t("Last error")}: {diagnostic.lastError}
</p>
) : null}
{scopeParts.length ? (
<p className="break-words text-xs [overflow-wrap:anywhere]">{scopeParts.join(" · ")}</p>
) : null}
{diagnostic.source ? (
<p className="break-words text-xs [overflow-wrap:anywhere]">
{t("Source")}: {diagnostic.source}
</p>
) : null}
{diagnostic.historicalStallTimeouts !== undefined ? (
<div className="space-y-1 text-xs">
<p className="text-foreground">
{t("Historical internode stall timeouts")}: {diagnostic.historicalStallTimeouts}
</p>
<p>
{t("This lifetime counter has no sampling window and does not indicate current degradation by itself.")}
</p>
{hasSupportingDetail ? (
<div className="flex flex-col gap-1 text-xs">
{diagnostic.lastSuccessfulUpdate || showLastSuccessfulUpdate ? (
<p>
{t("Last successful update")}:{" "}
{diagnostic.lastSuccessfulUpdate
? formatTimestamp(diagnostic.lastSuccessfulUpdate, locale)
: t("Unknown")}
</p>
) : null}
{diagnostic.lastError ? (
<p className="break-words [overflow-wrap:anywhere]">
{t("Last error")}: {diagnostic.lastError}
</p>
) : null}
{scopeParts.length ? (
<p className="break-words [overflow-wrap:anywhere]">{scopeParts.join(" · ")}</p>
) : null}
{diagnostic.source ? (
<p className="break-words [overflow-wrap:anywhere]">
{t("Source")}: <code>{diagnostic.source}</code>
</p>
) : null}
{diagnostic.historicalStallTimeouts !== undefined ? (
<>
<p className="text-foreground">
{t("Historical internode stall timeouts")}: {diagnostic.historicalStallTimeouts}
</p>
<p>
{t(
"This lifetime counter has no sampling window and does not indicate current degradation by itself.",
)}
</p>
</>
) : null}
{diagnostic.hint ? (
<p className="break-words [overflow-wrap:anywhere]">
{t("Backend guidance")}: {diagnostic.hint}
</p>
) : null}
{troubleshooting ? (
<p className="text-foreground">
{t("Troubleshooting")}: {troubleshooting}
</p>
) : null}
{troubleshootingHref ? (
<a
className="w-fit font-medium text-foreground underline underline-offset-4"
href={troubleshootingHref}
target="_blank"
rel="noreferrer"
>
{t("Open the real multi-node metrics verification guide")}
</a>
) : null}
</div>
) : null}
{diagnostic.hint ? (
<p className="break-words text-xs [overflow-wrap:anywhere]">
{t("Backend guidance")}: {diagnostic.hint}
</p>
) : null}
{troubleshooting ? (
<p className="text-xs text-foreground">
{t("Troubleshooting")}: {troubleshooting}
</p>
) : null}
{troubleshootingHref ? (
<a
className="w-fit text-xs font-medium text-foreground underline underline-offset-4"
href={troubleshootingHref}
target="_blank"
rel="noreferrer"
>
{t("Open the real multi-node metrics verification guide")}
</a>
) : null}
</dd>
</div>
)
}
function PerformanceStatusSourcesContent({
diagnostics,
usageFreshness,
t,
locale,
}: {
diagnostics: ClusterDiagnostics
usageFreshness?: StatusDiagnostic
t: Translate
locale?: string
}) {
const rows = [
{ label: t("Peer Health"), diagnostic: diagnostics.peerHealth },
{ label: t("Storage Readiness"), diagnostic: diagnostics.storageReadiness },
{
label: t("Usage Freshness"),
diagnostic: usageFreshness ?? diagnostics.usageFreshness,
showLastSuccessfulUpdate: true,
},
{
label: t("Listing and Metacache"),
diagnostic: diagnostics.listingHealth,
troubleshooting: t(
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
),
troubleshootingHref: "https://github.com/rustfs/backlog/issues/1392#issuecomment-5040442761",
},
{ label: t("Workload Admission"), diagnostic: diagnostics.workloadAdmission },
]
const hasAttention = rows.some(({ diagnostic }) => ["degraded", "stale", "unknown"].includes(diagnostic.state))
const reportedRows = rows.filter(({ diagnostic }) => diagnostic.state !== "not_reported")
const attentionCount = rows.filter(({ diagnostic }) =>
["degraded", "stale", "unknown"].includes(diagnostic.state),
).length
const [open, setOpen] = React.useState(hasAttention)
const previousHasAttention = React.useRef(hasAttention)
React.useEffect(() => {
if (hasAttention && !previousHasAttention.current) setOpen(true)
previousHasAttention.current = hasAttention
}, [hasAttention])
return (
<Collapsible open={open} onOpenChange={setOpen} className="group/diagnostics">
<Card className="gap-0 py-0 shadow-none">
<CardHeader className="grid-cols-[minmax(0,1fr)_auto] gap-4 py-4">
<div className="min-w-0">
<h2 id="diagnostic-details-title" className="text-base font-semibold">
{t("Diagnostic Details")}
</h2>
<CardDescription>
{hasAttention
? `${attentionCount} ${t("Diagnostic items need confirmation")}`
: reportedRows.length
? t("All reported diagnostics are healthy.")
: t("No diagnostic sources were reported by the server.")}
</CardDescription>
</div>
<CollapsibleTrigger
render={
<Button
type="button"
variant="ghost"
size="sm"
className="min-h-11 self-start sm:min-h-0"
aria-controls="diagnostic-details-content"
>
<span className="group-data-[state=open]/diagnostics:hidden">{t("Expand")}</span>
<span className="hidden group-data-[state=open]/diagnostics:inline">{t("Collapse")}</span>
<RiArrowDownSLine
data-icon="inline-end"
className="transition-transform duration-200 group-data-[state=open]/diagnostics:rotate-180"
aria-hidden
/>
</Button>
}
/>
</CardHeader>
<CollapsibleContent id="diagnostic-details-content">
<Separator />
<CardContent className="py-1">
<dl aria-labelledby="diagnostic-details-title">
{rows.map((row, index) => (
<React.Fragment key={row.label}>
{index ? <Separator /> : null}
<DiagnosticRow
label={row.label}
diagnostic={row.diagnostic}
troubleshooting={row.troubleshooting}
troubleshootingHref={row.troubleshootingHref}
showLastSuccessfulUpdate={row.showLastSuccessfulUpdate}
t={t}
locale={locale}
/>
</React.Fragment>
))}
</dl>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
)
}
export function PerformanceStatusSources({
diagnostics,
usageFreshness,
@@ -138,55 +264,9 @@ export function PerformanceStatusSources({
t: Translate
locale?: string
}) {
const notReported: StatusDiagnostic = { state: "not_reported" }
const peerHealth = diagnostics?.peerHealth ?? notReported
const storageReadiness = diagnostics?.storageReadiness ?? notReported
const resolvedUsageFreshness = usageFreshness ?? diagnostics?.usageFreshness ?? notReported
const listingHealth = diagnostics?.listingHealth ?? notReported
const workloadAdmission = diagnostics?.workloadAdmission ?? notReported
const rows = [
{ label: t("Peer Health"), diagnostic: peerHealth },
{ label: t("Storage Readiness"), diagnostic: storageReadiness },
{ label: t("Usage Freshness"), diagnostic: resolvedUsageFreshness, showLastSuccessfulUpdate: true },
{
label: t("Listing and Metacache"),
diagnostic: listingHealth,
troubleshooting: t(
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
),
troubleshootingHref: "https://github.com/rustfs/backlog/issues/1392#issuecomment-5040442761",
},
{ label: t("Workload Admission"), diagnostic: workloadAdmission },
]
if (!diagnostics) return null
return (
<Card className="shadow-none">
<CardHeader>
<h2 id="status-sources-title" className="text-base font-semibold">
{t("Status Sources")}
</h2>
<CardDescription>
{t("Review peer, storage, usage, listing, and workload admission health independently.")}
</CardDescription>
</CardHeader>
<CardContent>
<dl aria-labelledby="status-sources-title">
{rows.map((row, index) => (
<React.Fragment key={row.label}>
{index ? <Separator /> : null}
<DiagnosticRow
label={row.label}
diagnostic={row.diagnostic}
troubleshooting={row.troubleshooting}
troubleshootingHref={row.troubleshootingHref}
showLastSuccessfulUpdate={row.showLastSuccessfulUpdate}
t={t}
locale={locale}
/>
</React.Fragment>
))}
</dl>
</CardContent>
</Card>
<PerformanceStatusSourcesContent diagnostics={diagnostics} usageFreshness={usageFreshness} t={t} locale={locale} />
)
}
@@ -0,0 +1,75 @@
"use client"
import { RiCheckboxCircleLine, RiErrorWarningLine, RiQuestionLine } from "@remixicon/react"
import type { RunningStatusTopology, StatusDiagnostic } from "@/lib/performance-data"
import { getInfrastructureHealthState } from "./performance-infrastructure-card"
type Translate = (key: string) => string
export function PerformanceStatusSummary({
onlineServers,
offlineServers,
degradedServers,
initializingServers,
unknownServers,
onlineDisks,
offlineDisks,
unknownDisks,
topology,
peerHealth,
storageReadiness,
t,
}: {
onlineServers?: number
offlineServers?: number
degradedServers?: number
initializingServers?: number
unknownServers?: number
onlineDisks?: number
offlineDisks?: number
unknownDisks?: number
topology: RunningStatusTopology
peerHealth?: StatusDiagnostic
storageReadiness?: StatusDiagnostic
t: Translate
}) {
const healthState = getInfrastructureHealthState({
onlineServers,
offlineServers,
degradedServers,
initializingServers,
unknownServers,
onlineDisks,
offlineDisks,
unknownDisks,
topology,
peerHealth,
storageReadiness,
})
const Icon =
healthState === "attention" ? RiErrorWarningLine : healthState === "unknown" ? RiQuestionLine : RiCheckboxCircleLine
const label =
healthState === "attention"
? t("Cluster needs attention")
: healthState === "unknown"
? t("Cluster status is incomplete")
: t("Cluster is healthy")
return (
<section
className="flex flex-col gap-3 border bg-card p-4 sm:flex-row sm:items-center sm:justify-between"
aria-labelledby="running-status-summary-title"
>
<div className="flex min-w-0 items-center gap-3">
<Icon className="size-5 shrink-0" aria-hidden />
<h2 id="running-status-summary-title" className="font-semibold">
{label}
</h2>
</div>
<p className="text-xs text-muted-foreground tabular-nums">
{t("Servers")}: {onlineServers ?? t("Unknown")} {t("Online")} · {t("Disks")}: {onlineDisks ?? t("Unknown")}{" "}
{t("Online")}
</p>
</section>
)
}
@@ -2,8 +2,10 @@
import { RiDatabase2Line } from "@remixicon/react"
import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { niceBytes } from "@/lib/functions"
import type { StatusDiagnostic } from "@/lib/performance-data"
export interface UsageStat {
label: string
@@ -16,6 +18,7 @@ export function PerformanceUsageCard({
totalUsedCapacity,
usedPercent,
usageStats,
usageFreshness,
t,
}: {
totalCapacity?: number
@@ -23,9 +26,11 @@ export function PerformanceUsageCard({
totalUsedCapacity?: number
usedPercent?: number
usageStats: UsageStat[]
usageFreshness?: StatusDiagnostic
t: (key: string) => string
}) {
const formatCapacity = (value?: number) => (value === undefined ? t("Unknown") : niceBytes(String(value)))
const freshnessNeedsAttention = usageFreshness?.state === "degraded" || usageFreshness?.state === "stale"
return (
<Card className="h-full shadow-none">
@@ -73,6 +78,17 @@ export function PerformanceUsageCard({
</div>
</dl>
{freshnessNeedsAttention ? (
<div className="flex flex-col gap-2 border-s-2 border-current ps-3 text-muted-foreground">
<Badge variant={usageFreshness.state === "degraded" ? "destructive" : "default"}>
{usageFreshness.state === "degraded" ? t("Degraded") : t("Stale")}
</Badge>
<p className="break-words text-xs [overflow-wrap:anywhere]">
{usageFreshness.reason ?? t("Previously reported data may be out of date.")}
</p>
</div>
) : null}
<dl className="grid gap-4 sm:grid-cols-3">
{usageStats.map((item) => (
<div key={item.label} className="min-w-0">
+23 -4
View File
@@ -24,6 +24,7 @@ import { PerformanceInfrastructureCard } from "../_components/performance-infras
import { PerformanceServerList } from "../_components/performance-server-list"
import { PerformanceSummaryCards } from "../_components/performance-summary-cards"
import { PerformanceStatusSources } from "../_components/performance-status-sources"
import { PerformanceStatusSummary } from "../_components/performance-status-summary"
import { PerformanceUsageCard } from "../_components/performance-usage-card"
function formatDuration(seconds: number | undefined, t: (key: string) => string) {
@@ -258,11 +259,19 @@ export default function PerformancePage() {
</div>
) : null}
<PerformanceStatusSources
diagnostics={diagnosticsInfo}
usageFreshness={diagnosticsInfo ? usageFreshness : undefined}
<PerformanceStatusSummary
onlineServers={serverSummary?.online}
offlineServers={serverSummary?.offline}
degradedServers={serverSummary?.degraded}
initializingServers={serverSummary?.initializing}
unknownServers={serverSummary?.unknown}
onlineDisks={systemInfo.backend?.onlineDisks}
offlineDisks={systemInfo.backend?.offlineDisks}
unknownDisks={systemInfo.backend?.unknownDisks}
topology={runningStatus.topology}
peerHealth={diagnosticsInfo?.peerHealth}
storageReadiness={diagnosticsInfo?.storageReadiness}
t={t}
locale={i18n.resolvedLanguage}
/>
<div className="grid gap-4 xl:grid-cols-2">
@@ -277,6 +286,8 @@ export default function PerformancePage() {
offlineDisks={systemInfo.backend?.offlineDisks}
unknownDisks={systemInfo.backend?.unknownDisks}
topology={runningStatus.topology}
peerHealth={diagnosticsInfo?.peerHealth}
storageReadiness={diagnosticsInfo?.storageReadiness}
t={t}
/>
</div>
@@ -288,6 +299,7 @@ export default function PerformancePage() {
totalUsedCapacity={totalUsedCapacity}
usedPercent={usedPercent}
usageStats={usageStats}
usageFreshness={diagnosticsInfo ? usageFreshness : undefined}
t={t}
/>
</div>
@@ -305,6 +317,13 @@ export default function PerformancePage() {
<PerformanceSummaryCards metrics={summaryMetrics} title={t("Inventory")} />
<PerformanceBackendCard items={backendInfo} t={t} />
<PerformanceStatusSources
diagnostics={diagnosticsInfo}
usageFreshness={diagnosticsInfo ? usageFreshness : undefined}
t={t}
locale={i18n.resolvedLanguage}
/>
</div>
</Page>
)
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1554,5 +1554,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "Backend guidance",
"Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.",
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently."
"Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.",
"Diagnostic Details": "Diagnostic Details",
"Diagnostic items need confirmation": "diagnostic items need confirmation",
"All reported diagnostics are healthy.": "All reported diagnostics are healthy.",
"No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.",
"Expand": "Expand",
"Collapse": "Collapse",
"Cluster is healthy": "Cluster is healthy",
"Cluster needs attention": "Cluster needs attention",
"Cluster status is incomplete": "Cluster status is incomplete"
}
+10 -1
View File
@@ -1549,5 +1549,14 @@
"Backend guidance": "后端提示",
"Open the real multi-node metrics verification guide": "打开真实多节点指标确认指南",
"Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "在将列表症状判断为磁盘故障前,请关联时间窗口内的 walk_dir 指标和元数据缓存日志。",
"Review peer, storage, usage, listing, and workload admission health independently.": "分别检查节点、存储、用量、列表服务和工作负载准入状态。"
"Review peer, storage, usage, listing, and workload admission health independently.": "分别检查节点、存储、用量、列表服务和工作负载准入状态。",
"Diagnostic Details": "诊断详情",
"Diagnostic items need confirmation": "项诊断状态需要确认",
"All reported diagnostics are healthy.": "所有已报告的诊断均正常。",
"No diagnostic sources were reported by the server.": "服务器未报告诊断来源。",
"Expand": "展开",
"Collapse": "收起",
"Cluster is healthy": "集群运行正常",
"Cluster needs attention": "集群需要关注",
"Cluster status is incomplete": "集群状态信息不完整"
}
@@ -22,6 +22,10 @@ test("performance infrastructure card renders unknown, degraded, and initializin
assert.match(source, /initializingServers\?: number/)
assert.match(source, /unknownDisks\?: number/)
assert.match(source, /topology: RunningStatusTopology/)
assert.match(source, /peerHealth\?: StatusDiagnostic/)
assert.match(source, /storageReadiness\?: StatusDiagnostic/)
assert.match(source, /diagnosticNeedsAttention/)
assert.match(source, /diagnosticUncertain/)
assert.match(source, /hasIncompleteTopology/)
assert.match(source, /t\("Unreported"\)/)
assert.match(source, /\{t\("Unknown"\)\}/)
@@ -30,6 +34,18 @@ test("performance infrastructure card renders unknown, degraded, and initializin
assert.match(source, /value \?\? unavailableLabel/)
})
test("status page routes diagnostics into the relevant operational surfaces", () => {
const source = fs.readFileSync("app/(dashboard)/status/page.tsx", "utf8")
const usageSource = fs.readFileSync("app/(dashboard)/_components/performance-usage-card.tsx", "utf8")
assert.match(source, /peerHealth=\{diagnosticsInfo\?\.peerHealth\}/)
assert.match(source, /storageReadiness=\{diagnosticsInfo\?\.storageReadiness\}/)
assert.match(source, /usageFreshness=\{diagnosticsInfo \? usageFreshness : undefined\}/)
assert.match(usageSource, /usageFreshness\?: StatusDiagnostic/)
assert.match(usageSource, /usageFreshness\?\.state === "degraded"/)
assert.match(usageSource, /usageFreshness\?\.state === "stale"/)
})
test("performance server list treats every health state as a first-class filter", () => {
const source = fs.readFileSync("app/(dashboard)/_components/performance-server-list.tsx", "utf8")
+12 -1
View File
@@ -88,6 +88,16 @@ test("status sections expose semantic headings and named progress", () => {
assert.match(backendSource, /<h2/)
assert.match(backendSource, /Storage Configuration/)
assert.match(statusSourcesSource, /<h2/)
assert.match(statusSourcesSource, /<Collapsible/)
assert.match(statusSourcesSource, /<CollapsibleTrigger/)
assert.match(statusSourcesSource, /<CollapsibleContent/)
assert.match(statusSourcesSource, /if \(!diagnostics\) return null/)
assert.match(statusSourcesSource, /\["degraded", "stale", "unknown"\]\.includes\(diagnostic\.state\)/)
assert.match(statusSourcesSource, /open=\{open\}/)
assert.match(statusSourcesSource, /onOpenChange=\{setOpen\}/)
assert.match(statusSourcesSource, /hasAttention && !previousHasAttention\.current/)
assert.match(statusSourcesSource, /Diagnostic Details/)
assert.match(statusSourcesSource, /Diagnostic items need confirmation/)
assert.match(statusSourcesSource, /Peer Health/)
assert.match(statusSourcesSource, /Storage Readiness/)
assert.match(statusSourcesSource, /Usage Freshness/)
@@ -99,7 +109,8 @@ test("status sections expose semantic headings and named progress", () => {
assert.match(statusSourcesSource, /<Badge/)
assert.match(statusSourcesSource, /<Separator/)
assert.match(statusSourcesSource, /Correlate time-windowed walk_dir metrics and metacache logs/)
assert.doesNotMatch(pageSource, /\{diagnosticsInfo \? \(/)
assert.doesNotMatch(statusSourcesSource, /const notReported/)
assert.ok(pageSource.indexOf("<PerformanceServerList") < pageSource.indexOf("<PerformanceStatusSources"))
})
test("running status uses backend usable free capacity without synthesizing it", () => {