fix(daily-volume): 30/90/365 天切换"没反应";/logs/stats 加短缓存
CI / Backend (go vet + test) (push) Canceled after 0s
CI / Frontend (lint + build + test) (push) Canceled after 0s

=== 1. 切换范围没反应(真 bug)===
现象:7/30/90/365 天切换后图完全一样。
根因:GetDailyVolumeHistory 只返回**已抓到的快照**。平台只有 4 天数据,
      所以任何 days 都只返回 4 个点 → 图表一模一样。
修复:始终返回 days 个点,缺失的天用 Missing=true 标记。
      实测:days=7→7 点(缺3)、30→30 点(缺26)、90→90(缺86)、365→365(缺361)。

为什么必须区分 Missing 和"0 字节":
  前者是"平台没留存",后者是"那天确实没日志"。
  混在一起会让 30/90 天的图上出现大片 0,被误读成"日志量暴跌/系统停摆"。
前端:缺失的天画灰色浅柱,tooltip 显示"未留存",图下方提示
      "其中 N 天平台没有留存记录(灰色柱),不是当天没有日志"。

=== 2. /logs/stats 加 90 秒缓存 ===
分析页每次进入/切范围都打 index/stats,实测 5 分钟窗口冷查询 1.2 秒
(60 分钟更慢)。这类聚合值可容忍分钟级陈旧,加与指标查询一致的缓存:
  90s TTL + 时间窗按 2 分钟取整 + 上限 200 条 + 过期清理
实测:重复请求 1227ms → 8ms。

=== 3. 顺带确认已生效的优化 ===
  histogram(24h)   : 54ms(volume_range)
  daily-volume     : 10ms(只读库)
  stats 二次请求    : 8ms(新缓存)
  长范围(>1h)       : 不跑 count_over_time(预聚合快照 + 跳过内容扫描)

Pod = Hub latest = 12453ea723c1
This commit is contained in:
opsx committed 2026-10-30 16:46:40 +00:00
1 parent 5c7a3ec81d
commit 7b1ffd0274
7 files changed
+118 -26

No files matched your search

+2
View File
@@ -26,6 +26,8 @@ export interface DailyVolumePoint {
entries: number
/** 当天未结束或抓取时数据不全 */
partial: boolean
/** 该天平台没有留存快照(超过留存期/当天平台没运行)——与"日志量真的是 0"不同 */
missing?: boolean
}
export const logsApi = {
sources: () => http.get<PageData<LogSource>>('/logs/sources'),
+2
View File
@@ -52,6 +52,8 @@ export default {
other: 'Other',
},
daily: {
notRetained: 'Not retained',
missingHint: '{{count}} day(s) have no retained snapshot (grey bars) — that does NOT mean zero logs on those days.',
fixedBadge: 'Platform history',
badge: 'Platform history',
title: 'Daily volume',
+2
View File
@@ -52,6 +52,8 @@ export default {
other: '其他',
},
daily: {
notRetained: '未留存',
missingHint: '其中 {{count}} 天平台没有留存记录(灰色柱),不是"当天没有日志"。',
fixedBadge: '平台留存历史',
badge: '平台留存历史',
title: '每日日志量',
@@ -29,10 +29,15 @@ export const DailyVolumeChart = memo(function DailyVolumeChart({ points, days, o
bytes: point.bytes,
entries: point.entries,
partial: point.partial,
missing: Boolean(point.missing),
})), [points])
const hasData = data.some(item => item.bytes > 0)
const coverage = points.length
// 有留存的天数与"完全没留存"的天数分开统计:
// 后者是"不知道",不能画成 0 让人以为那天没日志。
const covered = data.filter(item => !item.missing)
const missingCount = data.length - covered.length
const hasData = covered.some(item => item.bytes > 0)
const coverage = covered.length
return (
<div className="space-y-3">
@@ -68,8 +73,9 @@ export const DailyVolumeChart = memo(function DailyVolumeChart({ points, days, o
<YAxis tick={{ fontSize: 11 }} width={64} tickFormatter={(value: number) => formatBytes(value)} />
<Tooltip
formatter={(value: number, _name, item) => {
const partial = (item?.payload as { partial?: boolean } | undefined)?.partial
return [`${formatBytes(value)}${partial ? ` · ${t('daily.inProgress')}` : ''}`, t('daily.seriesVolume')]
const payload = item?.payload as { partial?: boolean; missing?: boolean } | undefined
if (payload?.missing) return [t('daily.notRetained'), t('daily.seriesVolume')]
return [`${formatBytes(value)}${payload?.partial ? ` · ${t('daily.inProgress')}` : ''}`, t('daily.seriesVolume')]
}}
/>
{/* 圆柱形柱子:顶部圆角 + 渐变,当天未结束的用空心区分 */}
@@ -77,7 +83,13 @@ export const DailyVolumeChart = memo(function DailyVolumeChart({ points, days, o
{data.map(item => (
<Cell
key={item.date}
fill={item.partial ? 'hsl(var(--info) / 0.35)' : 'url(#dailyVolumeBar)'}
fill={
item.missing
? 'hsl(var(--muted-foreground) / 0.12)'
: item.partial
? 'hsl(var(--info) / 0.35)'
: 'url(#dailyVolumeBar)'
}
stroke={item.partial ? 'hsl(var(--info))' : undefined}
strokeDasharray={item.partial ? '3 3' : undefined}
/>
@@ -89,6 +101,14 @@ export const DailyVolumeChart = memo(function DailyVolumeChart({ points, days, o
) : (
<EmptyChart message={t('daily.empty')} />
)}
{/* 灰柱 = 平台没有留存那天(不是"日志量为 0"),必须显式说明,
否则 30/90/365 天视角下会被误读成"以前日志很少" */}
{missingCount > 0 && (
<p className="text-[11px] text-muted-foreground">
{t('daily.missingHint', { count: missingCount })}
</p>
)}
</div>
)
})