perf(daily-volume): 每日量改用 volume_range(20s → 54ms),图表改圆柱
CI / Backend (go vet + test) (push) Canceled after 0s
CI / Frontend (lint + build + test) (push) Canceled after 0s

=== 核心技巧:体积用 index/volume_range,不要用 index/stats ===
同为 24 小时、同一 selector,两条索引查询差 370 倍:
    index/stats      : 整天聚合,实测 20s 直接超时(之前每日量接口卡 20s 的根因)
    index/volume_range: 按 step 分桶只累加 chunk 字节,实测 54ms,97 个桶

原因:index/stats 要跨全部 chunk 去重统计 streams/entries;
volume_range 只把索引里记录的 chunk 字节数按时间桶求和,不解码内容。

=== 后端改动 ===
services/daily_volume.go:
  体积:LokiHistogram(走 volume_range,step 由跨度自动选)→ 累加 54ms
  条数:只能 index/stats,降级为「尽力而为」25s 超时;
        拿不到就保留上一次的值(不能因为一次超时把历史条数清零),
        绝不让它拖住体积入库。
  前端图只画体积,条数缺失不影响可读性。

=== 前端改动 ===
DailyVolumeChart:AreaChart(面积)→ BarChart(圆柱)
  顶部圆角 radius=[6,6,0,0]、渐变填充、maxBarSize=44
  当天未结束的柱子用半透明 + 虚线描边区分,避免把"才过半天"误读成"量掉了"

=== 实测 ===
  24h volume_range: 54ms,97 桶,合计 369.61 GB(与全集群 ~370GB/天 吻合)
  /logs/daily-volume 接口: 10ms(读库)
  Pod = Hub latest = 4cdc8b9c4841
This commit is contained in:
opsx committed 2026-10-30 16:40:49 +00:00
1 parent d0adc095dd
commit 5c7a3ec81d
2 files changed
+58 -25

No files matched your search

@@ -1,5 +1,5 @@
import { memo, useMemo } from 'react'
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
import { Bar, BarChart, Cell, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button'
import type { DailyVolumePoint } from '@/api/logs'
@@ -56,11 +56,11 @@ export const DailyVolumeChart = memo(function DailyVolumeChart({ points, days, o
{hasData ? (
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={data} margin={{ top: 12, right: 16, bottom: 4, left: 0 }}>
<BarChart data={data} margin={{ top: 12, right: 16, bottom: 4, left: 0 }} barCategoryGap="18%">
<defs>
<linearGradient id="dailyVolumeFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="hsl(var(--info))" stopOpacity={0.35} />
<stop offset="100%" stopColor="hsl(var(--info))" stopOpacity={0.02} />
<linearGradient id="dailyVolumeBar" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="hsl(var(--info))" stopOpacity={0.95} />
<stop offset="100%" stopColor="hsl(var(--info))" stopOpacity={0.45} />
</linearGradient>
</defs>
<CartesianGrid stroke="hsl(var(--border))" strokeDasharray="3 3" />
@@ -72,15 +72,18 @@ export const DailyVolumeChart = memo(function DailyVolumeChart({ points, days, o
return [`${formatBytes(value)}${partial ? ` · ${t('daily.inProgress')}` : ''}`, t('daily.seriesVolume')]
}}
/>
<Area
type="monotone"
dataKey="bytes"
stroke="hsl(var(--info))"
strokeWidth={2}
fill="url(#dailyVolumeFill)"
name={t('daily.seriesVolume')}
/>
</AreaChart>
{/* 圆柱形柱子:顶部圆角 + 渐变,当天未结束的用空心区分 */}
<Bar dataKey="bytes" name={t('daily.seriesVolume')} radius={[6, 6, 0, 0]} maxBarSize={44}>
{data.map(item => (
<Cell
key={item.date}
fill={item.partial ? 'hsl(var(--info) / 0.35)' : 'url(#dailyVolumeBar)'}
stroke={item.partial ? 'hsl(var(--info))' : undefined}
strokeDasharray={item.partial ? '3 3' : undefined}
/>
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
) : (