refactor(analysis): 去掉排行 10/30/50 选择器,改为一次取 50 + 前端分页;零点结算昨天
=== 1. 为什么去掉「显示条数」选择器 === topk 的 N 不影响查询成本(扫描的 chunk 集合完全相同),实测: topk(10) 17833ms / 11 条 topk(30) 18019ms / 34 条 topk(50) 7394ms / 54 条 topk(100) 17553ms / 74 条 波动来自集群负载,与 N 无关 —— 它只决定最后返回多少序列。 所以"选 30"既不更快也不更慢,本质是个翻页开关; 而选它还要重新等一次十几秒的查询,纯亏。 改为:一次 topk(50),前端 10 条/页 → 5 页只需 1 次查询。 附带收益:占比的分母从"前 10 合计"变成"前 50 合计",更接近真实占比。 副标题明确写清"一次取前 50 个应用(占比按这 50 个合计计算)"。 === 2. 零点结算昨天(daily volume)=== 原来只有 30 分钟 ticker 回补,且启动时间不对齐整点 → 昨天最早的一段可能已经超出 Loki 24h 保留期被删, 结算结果系统性偏低,图上却标"完整一天",属静默错数。 新增 nextMidnightFinalize():每天 00:01 触发 FinalizeYesterdayVolume() - 昨天定稿 partial=false - 补一次条数(index/stats,一天一次可接受) - 顺带刷新今天 partial=true 的柱子 ticker 仍保留,负责当天数据的 30 分钟刷新。 === 3. 顺带 === - 去掉每日量"其中 N 天平台没有留存记录"的提示文案(按用户要求) - 修 QuickQueryTemplates.tsx 的未使用变量导致 tsc -b 失败 - 工作区里上一轮中断的"排行体积列"改动一并纳入(app-ranking 返回 bytes, TopAppsTable 增加体积列,快照记录 per-app 字节数) Pod = Hub latest = a16978d2f88b
This commit is contained in:
1 parent
63dd491c5b
commit
d4422f1541
22 files changed
+715
-117
No files matched your search
@@ -170,10 +170,10 @@ func upsertVolumeSnapshot(sourceID uint, date string, bytes int64, partial bool)
|
||||
// 实测 6ms(空闲)~ 20s(有并发查询时)。每 30 分钟 × 每一天都重跑一遍,
|
||||
// 既浪费 Loki 的 CPU,又会和用户页面上的查询抢同一台单实例的算力。所以:
|
||||
//
|
||||
// • 只处理 Date < 今天 的天(今天没结束,条数没有意义)
|
||||
// • 每天最多 entriesMaxAttempts 次,两次间隔 ≥ entriesRetryInterval
|
||||
// • 尝试次数**先写库再查**:进程中途被杀也不会退化成"每 30 分钟重试"
|
||||
// • 拿不到就留 0,前端图只画体积,不影响可读性
|
||||
// - 只处理 Date < 今天 的天(今天没结束,条数没有意义)
|
||||
// - 每天最多 entriesMaxAttempts 次,两次间隔 ≥ entriesRetryInterval
|
||||
// - 尝试次数**先写库再查**:进程中途被杀也不会退化成"每 30 分钟重试"
|
||||
// - 拿不到就留 0,前端图只画体积,不影响可读性
|
||||
func captureDailyEntries(source *models.LogSource) {
|
||||
if source == nil {
|
||||
return
|
||||
@@ -311,16 +311,31 @@ func pruneDailyVolumeSnapshots() {
|
||||
today := dateKey(time.Now())
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
known := make(map[uint]bool, len(db.LogSources))
|
||||
for _, src := range db.LogSources {
|
||||
known[src.ID] = true
|
||||
}
|
||||
changed := false
|
||||
kept := make([]models.DailyVolumeSnapshot, 0, len(db.DailyVolumeSnapshots))
|
||||
droppedOrphan := 0
|
||||
for _, snap := range db.DailyVolumeSnapshots {
|
||||
// 超过留存期
|
||||
if snap.Date < cutoff {
|
||||
continue
|
||||
}
|
||||
// 数据源已被删除:它的快照没有任何入口能查到,留着只会混淆取证
|
||||
// (实测遇到过库里留着 id=2 的历史,查"到底抓没抓"时白查一轮)
|
||||
if !known[snap.SourceID] {
|
||||
droppedOrphan++
|
||||
continue
|
||||
}
|
||||
kept = append(kept, snap)
|
||||
}
|
||||
if droppedOrphan > 0 {
|
||||
log.Printf("[daily-volume] 清理已删除数据源的快照 %d 条", droppedOrphan)
|
||||
}
|
||||
if len(kept) != len(db.DailyVolumeSnapshots) {
|
||||
log.Printf("[daily-volume] 清理过期快照 %d 条", len(db.DailyVolumeSnapshots)-len(kept))
|
||||
log.Printf("[daily-volume] 清理过期快照 %d 条", len(db.DailyVolumeSnapshots)-len(kept)-droppedOrphan)
|
||||
db.DailyVolumeSnapshots = kept
|
||||
changed = true
|
||||
}
|
||||
@@ -414,10 +429,66 @@ func StartDailyVolumeCapture() {
|
||||
log.Printf("[daily-volume] 启动回补最近 %d 天", dailyVolumeBackfillDays)
|
||||
CaptureRecentDailyVolumes()
|
||||
|
||||
// 两条节奏:
|
||||
// 30 分钟 ticker → 刷新"今天"的柱子(当天数据一直在涨)
|
||||
// 零点后 1 分钟 → **结算昨天**(把它定稿成完整一天)
|
||||
//
|
||||
// 为什么零点结算必须对齐而不能靠 30 分钟 ticker:
|
||||
// Loki 保留期默认 24h,昨天 00:00 的数据在今天 00:00 之后就开始被删。
|
||||
// 如果等到 00:30 那次 ticker 才抓,昨天最早的 30 分钟已经没了,
|
||||
// 结算结果会系统性偏低,而图上又标着"完整一天",属于静默错数。
|
||||
// 00:01 结算时最多只丢 1 分钟,误差可忽略。
|
||||
ticker := time.NewTicker(30 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
CaptureRecentDailyVolumes()
|
||||
midnight := time.NewTimer(time.Until(nextMidnightFinalize()))
|
||||
defer midnight.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
CaptureRecentDailyVolumes()
|
||||
case <-midnight.C:
|
||||
FinalizeYesterdayVolume()
|
||||
midnight.Reset(time.Until(nextMidnightFinalize()))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// nextMidnightFinalize 返回下一个"零点后 1 分钟"的时间点。
|
||||
func nextMidnightFinalize() time.Time {
|
||||
now := time.Now()
|
||||
next := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 1, 0, 0, time.Local)
|
||||
return next
|
||||
}
|
||||
|
||||
// FinalizeYesterdayVolume 零点后结算昨天:把昨天定稿(partial=false),
|
||||
// 同时刷新今天的 partial=true 柱子。多数据源逐个处理,单个失败不影响其它。
|
||||
func FinalizeYesterdayVolume() {
|
||||
db := repository.GetDB()
|
||||
if db == nil {
|
||||
return
|
||||
}
|
||||
db.RLock()
|
||||
sources := make([]models.LogSource, 0, len(db.LogSources))
|
||||
for _, src := range db.LogSources {
|
||||
sources = append(sources, src)
|
||||
}
|
||||
db.RUnlock()
|
||||
|
||||
now := time.Now()
|
||||
yesterday := now.AddDate(0, 0, -1)
|
||||
for _, src := range sources {
|
||||
src := src
|
||||
if err := CaptureDailyVolume(&src, yesterday, false); err != nil {
|
||||
log.Printf("[daily-volume] 零点结算 %s %s 失败: %v", src.Name, dateKey(yesterday), err)
|
||||
continue
|
||||
}
|
||||
log.Printf("[daily-volume] 零点结算完成 %s %s", src.Name, dateKey(yesterday))
|
||||
// 昨天定稿后,条数也可以补一次(走 index/stats,慢但一天一次可接受)
|
||||
captureDailyEntries(&src)
|
||||
if err := CaptureDailyVolume(&src, now, true); err != nil {
|
||||
log.Printf("[daily-volume] 刷新今天 %s 失败: %v", src.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user