feat(performance): enhance uptime calculations and chart data handling
This commit is contained in:
@@ -60,6 +60,32 @@ function getChartThemeTokens(resolvedTheme: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const UPTIME_AXIS_MAX = 100
|
||||
const UPTIME_FOCUSED_AXIS_MIN = 95
|
||||
const UPTIME_MINOR_OUTAGE_AXIS_MIN = 90
|
||||
|
||||
function toUptimeChartValue(value: number): number {
|
||||
if (!Number.isFinite(value)) return 0
|
||||
return Math.min(UPTIME_AXIS_MAX, Math.max(0, value))
|
||||
}
|
||||
|
||||
function getUptimeAxisMin(values: number[]): number {
|
||||
const finiteValues = values.filter((value) => Number.isFinite(value))
|
||||
if (finiteValues.length === 0) return UPTIME_FOCUSED_AXIS_MIN
|
||||
|
||||
const minValue = Math.max(0, Math.min(...finiteValues))
|
||||
if (minValue >= UPTIME_FOCUSED_AXIS_MIN) return UPTIME_FOCUSED_AXIS_MIN
|
||||
if (minValue >= UPTIME_MINOR_OUTAGE_AXIS_MIN) {
|
||||
return UPTIME_MINOR_OUTAGE_AXIS_MIN
|
||||
}
|
||||
|
||||
return Math.max(0, Math.floor((minValue - 5) / 10) * 10)
|
||||
}
|
||||
|
||||
function stripUptimePointSuffix(value: string): string {
|
||||
return value.replace(/__(start|end)$/, '')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Latency trend chart (24h, multi-group point-line chart)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -173,12 +199,20 @@ export function UptimeTrendChart(props: {
|
||||
const spec = useMemo(() => {
|
||||
if (props.series.length === 0) return null
|
||||
|
||||
const data = props.series.map((point) => ({
|
||||
const rawData = props.series.map((point) => ({
|
||||
date: formatDayLabel(point.date),
|
||||
uptime: point.uptime_pct,
|
||||
uptime: toUptimeChartValue(point.uptime_pct),
|
||||
incidents: point.incidents,
|
||||
outage: point.outage_minutes,
|
||||
}))
|
||||
const data =
|
||||
rawData.length === 1
|
||||
? [
|
||||
{ ...rawData[0], date: `${rawData[0].date}__start` },
|
||||
{ ...rawData[0], date: `${rawData[0].date}__end` },
|
||||
]
|
||||
: rawData
|
||||
const axisMin = getUptimeAxisMin(rawData.map((point) => point.uptime))
|
||||
|
||||
return {
|
||||
type: 'line' as const,
|
||||
@@ -204,7 +238,9 @@ export function UptimeTrendChart(props: {
|
||||
},
|
||||
tooltip: {
|
||||
mark: {
|
||||
title: { value: (d: { date: string }) => d.date },
|
||||
title: {
|
||||
value: (d: { date: string }) => stripUptimePointSuffix(d.date),
|
||||
},
|
||||
content: [
|
||||
{
|
||||
key: t('Uptime'),
|
||||
@@ -225,6 +261,8 @@ export function UptimeTrendChart(props: {
|
||||
{
|
||||
orient: 'bottom',
|
||||
label: {
|
||||
formatMethod: (val: number | string) =>
|
||||
stripUptimePointSuffix(String(val)),
|
||||
style: { fill: textColor, fontSize: 10 },
|
||||
autoLimit: true,
|
||||
},
|
||||
@@ -232,8 +270,8 @@ export function UptimeTrendChart(props: {
|
||||
},
|
||||
{
|
||||
orient: 'left',
|
||||
min: 95,
|
||||
max: 100,
|
||||
min: axisMin,
|
||||
max: UPTIME_AXIS_MAX,
|
||||
label: {
|
||||
formatMethod: (val: number | string) => `${val}%`,
|
||||
style: { fill: textColor, fontSize: 10 },
|
||||
|
||||
@@ -79,6 +79,12 @@ type PerformanceRow = {
|
||||
avg_tps: number
|
||||
}
|
||||
|
||||
function toUptimePct(value: number): number {
|
||||
if (!Number.isFinite(value)) return 0
|
||||
const clamped = Math.min(100, Math.max(0, value))
|
||||
return Math.round(clamped * 100) / 100
|
||||
}
|
||||
|
||||
function toLatencySeries(groups: PerformanceGroup[]) {
|
||||
const byTs = new Map<number, number[]>()
|
||||
for (const group of groups) {
|
||||
@@ -107,8 +113,9 @@ function toUptimeSeries(groups: PerformanceGroup[]): UptimeDayPoint[] {
|
||||
for (const point of group.series) {
|
||||
const current = byTs.get(point.ts) ?? { rates: [], incidents: 0 }
|
||||
if (Number.isFinite(point.success_rate)) {
|
||||
current.rates.push(point.success_rate)
|
||||
if (point.success_rate < 100) current.incidents += 1
|
||||
const successRate = toUptimePct(point.success_rate)
|
||||
current.rates.push(successRate)
|
||||
if (successRate < 100) current.incidents += 1
|
||||
}
|
||||
byTs.set(point.ts, current)
|
||||
}
|
||||
@@ -123,7 +130,7 @@ function toUptimeSeries(groups: PerformanceGroup[]): UptimeDayPoint[] {
|
||||
: 0
|
||||
return {
|
||||
date: new Date(ts * 1000).toISOString(),
|
||||
uptime_pct: Math.round(uptime * 100) / 100,
|
||||
uptime_pct: toUptimePct(uptime),
|
||||
incidents: value.incidents,
|
||||
outage_minutes: 0,
|
||||
}
|
||||
@@ -131,12 +138,15 @@ function toUptimeSeries(groups: PerformanceGroup[]): UptimeDayPoint[] {
|
||||
}
|
||||
|
||||
function toGroupUptimeSeries(group: PerformanceGroup): UptimeDayPoint[] {
|
||||
return group.series.map((point) => ({
|
||||
date: new Date(point.ts * 1000).toISOString(),
|
||||
uptime_pct: Math.round(point.success_rate * 100) / 100,
|
||||
incidents: point.success_rate < 100 ? 1 : 0,
|
||||
outage_minutes: 0,
|
||||
}))
|
||||
return group.series.map((point) => {
|
||||
const successRate = toUptimePct(point.success_rate)
|
||||
return {
|
||||
date: new Date(point.ts * 1000).toISOString(),
|
||||
uptime_pct: successRate,
|
||||
incidents: successRate < 100 ? 1 : 0,
|
||||
outage_minutes: 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function average(
|
||||
|
||||
Reference in New Issue
Block a user