fix(alerts,analysis): 告警引擎稳定性 5 项修复;每日量接口去掉同步 Loki 查询(20s→10ms)
=== 一、告警引擎稳定性审查(引擎本身是异步的:ticker + goroutine)=== 1) 【高】ticker 无重叠保护 ticker 每 1 分钟触发;一轮规则检查 8 条 × 20~30s = 2~4 分钟,远超一分钟。 原来每过一分钟就再开一轮 → 查询越堆越多,抢走页面查询的 Loki 资源, 每轮越来越慢,最终表现为"告警不响了"。 → 新增 alertCheckRunning(atomic.Bool),上一轮未完成直接跳过本轮并记日志。 2) 【高】单条规则无超时 RunAlertCheck(context.Background()) 没有 deadline:Loki 一旦挂住,整轮永久运行, 叠加问题 1 会让引擎再也不启动新一轮(静默死亡)。 → 每轮总超时 5 分钟(alertRoundTimeout)+ 单条规则 90s(alertRuleTimeout)。 3) 【中】panic 只在整个 tick 层 recover 一条规则 panic 会让本轮后续规则全部跳过。 → per-rule recover,panic 记为一条 failed 投递记录,后续规则继续。 4) 【中】冷却记录每分钟写一条,把真实发送记录挤光 AlertDeliveries 只保留最近 50 条,而处于冷却期时每个 tick 都写一条 cooldown, 一小时内真实的"已发送"记录就全被挤掉了,发送历史没法看。 → recordCooldownThrottled():同一规则 10 分钟最多写一条。附单测。 5) 规则串行本身保留(避免同时压垮 Loki),但配合 1) 才安全。 === 二、每日日志量接口:20 秒 → 10 毫秒 === 根因:GetDailyVolumeHistory 里**同步** await 一次"整天 index/stats"补抓当天数据, 那次查询超时 20s → 整个接口 20s → 前端超时 → 页面显示"每日日志量没数据"; 更糟的是这条重查询还和页面的 histogram/stats 抢 Loki,导致那两个接口同时 502 (访问日志实证:daily-volume latency_ms 20000.997,stats/histogram 502)。 → 接口改为**只读库**,不触发任何 Loki 查询;当天数据由后台每 30 分钟的 定时任务补(CaptureRecentDailyVolumes)。 → 同时把整天 index/stats 的超时从 20s 提到 90s(370GB/天,20s 不够)。 实测:10ms。 === 三、通知通道改名「默认告警」(schemaVersion v8→v9) === "lark 默认告警" / "默认通知通道(待配置 Webhook)" → 统一为「默认告警」。 只改名,不动 Webhook、不动 enabled: 首次部署创建的占位通道必须保持「空 Webhook + 禁用」,由管理员自己填, 否则会出现"已启用但空地址"的通道,告警一命中就全部投递失败。 === 四、其它文案 === 「每日日志量」副标题精简为「每天的日志总量,平台保留历史趋势」, 并移除「平台留存历史」徽标(用户反馈多余)。 === 实测(303134c0c718)=== /logs/daily-volume:10ms(原 20000ms) 通道列表:id=1 name="默认告警" enabled=true hasWebhook=true go test ./... 全绿(含新增 TestRecordCooldownThrottled)
This commit is contained in:
1 parent
607b67a2ed
commit
cb1ef1dff4
9 files changed
+149
-36
No files matched your search
@@ -54,7 +54,7 @@ export default {
|
||||
fixedBadge: 'Platform history',
|
||||
badge: 'Platform history',
|
||||
title: 'Daily volume',
|
||||
subtitle: 'Daily totals kept by the platform (Loki only retains 24h, history lives here)',
|
||||
subtitle: 'Daily log volume — history kept by the platform',
|
||||
empty: 'No history yet — the platform records one snapshot per day',
|
||||
lastDays: '{{count}}d',
|
||||
oneYear: '1y',
|
||||
|
||||
@@ -54,7 +54,7 @@ export default {
|
||||
fixedBadge: '平台留存历史',
|
||||
badge: '平台留存历史',
|
||||
title: '每日日志量',
|
||||
subtitle: '每天的总量趋势,数据由平台每日留存(Loki 只保留 24 小时,历史在这里)',
|
||||
subtitle: '每天的日志总量,平台保留历史趋势',
|
||||
empty: '还没有历史数据,平台每天会自动记录一次',
|
||||
lastDays: '{{count}} 天',
|
||||
oneYear: '1 年',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { memo, useMemo } from 'react'
|
||||
import { Area, AreaChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { compactNumber } from './chart-shared'
|
||||
import type { AppRow } from '../hooks/useAnalysisMetrics'
|
||||
import { chartColors } from './chart-shared'
|
||||
import { EmptyChart } from './EmptyChart'
|
||||
@@ -25,8 +26,9 @@ export const AppStreamChart = memo(function AppStreamChart({ apps, appTimes }: {
|
||||
<AreaChart data={data} margin={{ top: 12, right: 16, bottom: 4, left: 0 }}>
|
||||
<CartesianGrid stroke="hsl(var(--border))" strokeDasharray="3 3" />
|
||||
<XAxis dataKey="time" tick={{ fontSize: 11 }} minTickGap={30} />
|
||||
<YAxis allowDecimals={false} tick={{ fontSize: 11 }} width={48} />
|
||||
<Tooltip />
|
||||
{/* 流图是"条数",不是字节:用 万/亿 缩写 + 条,避免裸数字看不懂 */}
|
||||
<YAxis tick={{ fontSize: 11 }} width={56} tickFormatter={(value: number) => compactNumber(value)} />
|
||||
<Tooltip formatter={(value: number) => [`${compactNumber(value)} 条`, '']} />
|
||||
<Legend wrapperStyle={{ fontSize: 11 }} />
|
||||
{topApps.map((app, index) => {
|
||||
const color = chartColors[index % chartColors.length]
|
||||
|
||||
@@ -288,10 +288,7 @@ const AnalysisPage = memo(function AnalysisPage() {
|
||||
<div ref={dailyView.ref}>
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<CardTitle className="text-base">{t('daily.title')}</CardTitle>
|
||||
<span className="rounded-full bg-success/10 px-2 py-0.5 text-[11px] font-medium text-success">{t('daily.badge')}</span>
|
||||
</div>
|
||||
<CardTitle className="text-base">{t('daily.title')}</CardTitle>
|
||||
<p className="text-xs text-muted-foreground">{t('daily.subtitle')}</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
||||
Reference in new issue
Block a user