=== 问题(实测)===
按应用排行必须读日志内容(Loki 3.4 的 index/volume 不支持 app 维度聚合,
aggregateBy=app 无效),成本 ∝ 时间范围:
5 分钟 ≈ 1.3GB → 9.8s
15 分钟 ≈ 3.9GB → 19.5s
1 小时 ≈ 15GB → 预计 60~90s
6 小时 ≈ 92GB → 5~8 分钟
24 小时 ≈ 370GB → 不可用
用户在时间范围里选 6h/24h 时,页面必然卡死。
=== 方案 ===
新增 models.AppVolumeSnapshot + services/app_volume.go:
- 后台任务每小时第 5 分钟抓「上一个完整小时」的 per-app 条数
- 用**瞬时查询**(/loki/api/v1/query + [1h])而不是 query_range:
单点评估、扫描量相同、省掉多步开销;并绕开 LokiMetrics 的 90s 上限
与重型查询信号量(后台任务不该和前台抢令牌)
- 每源每小时只留 top 50 应用,保留 90 天
- 幂等:同 (hour, source, ns, app) 更新而非重复插入
- 超出 Loki 26h 保留期的窗口跳过(抓也抓不到,白等几分钟)
新增 GET /api/v1/logs/app-ranking?sourceId&from&to&limit → 读库毫秒级
前端:analysis 判断范围 > 60 分钟就走预聚合接口,并停掉实时重查询
(rankingEnabled 置 false);表格副标题说明「读的是预聚合快照,已留存 N 小时,
不含错误数与趋势」。
=== 顺带修掉的根因 bug ===
useInView 原来用 useRef + useEffect([inView]):分析页首屏 data===null 时
内容块不渲染,ref.current 为 null,effect 直接 return;等数据到了 DOM 才挂上,
但依赖没变 → effect 不重跑 → IntersectionObserver 从未创建 → inView 永远 false
→ 重查询永不发出 → 页面一直显示"没有应用维度的数据"。
改成 callback ref(节点挂载即作为依赖),并加 scroll/resize 兜底 + 挂载瞬间量一次。
另外分析页加 8 秒无条件兜底:可见性判定再失灵,8 秒后也一定查一次。
默认时间范围 15 分钟 → 5 分钟(19.5s → 9.8s)。
=== 实测 ===
Pod = Hub latest = 98de87e7f895
5 分钟窗口 by-app:9.8s(75 序列);15 分钟:19.5s
34 lines
1.3 KiB
Go
34 lines
1.3 KiB
Go
package models
|
|
|
|
import "time"
|
|
|
|
// AppVolumeSnapshot 按小时留存「每个应用」的日志量(条数)。
|
|
//
|
|
// 为什么必须落库:
|
|
//
|
|
// Loki 的 index/volume 只支持 namespace 级聚合(3.4 实测 aggregateBy=app 不生效),
|
|
// 所以"按应用排行"只能读日志内容,成本 ∝ 时间范围:
|
|
// 5 分钟 ≈ 1.3GB ≈ 10 秒
|
|
// 15 分钟 ≈ 3.9GB ≈ 20 秒
|
|
// 6 小时 ≈ 92GB ≈ 5~8 分钟 ← 页面根本等不了
|
|
// 24 小时 ≈ 370GB ≈ 不可用
|
|
// 把 per-app 数据按小时在后台算好存起来,长范围排行就变成读库(毫秒级)。
|
|
//
|
|
// 采集方式:每小时第 5 分钟抓「上一个完整小时」,一条
|
|
//
|
|
// sum by (app, namespace)(count_over_time({...}[1h]))
|
|
//
|
|
// 查询在后台跑,慢一点无所谓;页面永远读库。
|
|
type AppVolumeSnapshot struct {
|
|
ID uint `json:"id"`
|
|
// Hour 小时桶,格式 YYYY-MM-DDTHH(本地时区,按整点对齐)
|
|
Hour string `json:"hour"`
|
|
SourceID uint `json:"sourceId"`
|
|
Namespace string `json:"namespace"`
|
|
App string `json:"app"`
|
|
Entries int64 `json:"entries"`
|
|
// Partial 该小时数据是否不完整(如平台停了一段时间后才回补,超出 Loki 保留期)
|
|
Partial bool `json:"partial"`
|
|
CapturedAt time.Time `json:"capturedAt"`
|
|
}
|