fix(analysis): 每日量不再懒加载;按应用统计期间显示 loading(不再假装"没有数据")
用户的三个现象,对应三个独立缺陷: === 1. 每日日志量"一直没数据" === 接口只读平台库,实测 **10ms**,却仍被 inView 懒加载门控。 ref 虽然挂在 DOM 上,但滚动判定在多层布局里不可靠 → enabled 一直 false → 请求根本不发 → 页面显示"暂无历史数据"。 改为:只要有数据源就加载(enabled = Boolean(sourceId))。 这个接口 10ms,没有任何理由懒加载。 === 2. 排行/流图"打开没有数据,等很久也没 loading" === 按应用统计要 10~15 秒(读日志内容,物理下限)。 但 loading 是**整页共用**的:索引类指标 100ms 就回来、 重查询还在跑时 loading 就已经变 false, 卡片于是渲染成"没有应用维度的数据"——用户以为查完了、没数据。 改为: - 新增 appsLoading,专门标记"按应用统计"这段重查询 - 卡片在 appsLoading 期间显示 spinner + "正在按应用统计…" - loading 与 appsLoading 都在 finally 清理(成功/失败/取消都清) === 3. data 为 null 时整页空白 === 原来 `) : !data || !sourceId ? null : (` 直接 return null, 首屏或重查询异常时是**整页空白**,和"没数据"无法区分。 改为显示 spinner + "正在按应用统计(需读取日志内容,通常 10~30 秒)"。 === 关于"用 Go 定时任务统计每日总量"=== 这个机制**早就存在**(services/daily_volume.go): - 启动 20s 后回补 + 每 30 分钟一次 ticker - 用 index/stats 取整天的条数与字节,**不读日志内容**,通常秒级 - 快照写入平台库,保留 400 天 实测当前库内已有 4 天数据(10ms 返回): 2026-10-27 32.5 GB 2026-10-28 23.9 GB 2026-10-29 133.6 GB 2026-10-30 239.3 GB 问题只在"前端没去请求",不在采集侧。 实测(df3b477e6740):每日量接口 10ms 返回 4 天,Pod 1/1 Running。
This commit is contained in:
1 parent
419a81c2af
commit
a14bdb5e07
2 files changed
+24
-6
No files matched your search
@@ -75,6 +75,11 @@ export function useAnalysisMetrics({ sourceId, start, end, namespace, rankingEna
|
||||
const [appLabel, setAppLabel] = useState('app')
|
||||
const [data, setData] = useState<AnalysisData | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
// appsLoading 单独跟踪"按应用统计"这段重查询(10~30 秒)。
|
||||
// 它与 loading 分开的原因:上面索引类指标 100ms 就回来了,
|
||||
// 如果共用一个 loading,页面会在"索引已好、app 还没好"的空档里
|
||||
// 显示成"没有应用维度的数据"——用户等了很久以为没数据,其实还在算。
|
||||
const [appsLoading, setAppsLoading] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [refreshTick, setRefreshTick] = useState(0)
|
||||
const abortRef = useRef<AbortController | null>(null)
|
||||
@@ -192,6 +197,7 @@ export function useAnalysisMetrics({ sourceId, start, end, namespace, rankingEna
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
setAppsLoading(true)
|
||||
|
||||
// 这两条并行而不是串行:实测总量 22s、error 8.6s,串行 31s,并行 22s。
|
||||
// 并发上限是 4,两条并行不会撞护栏(之前串行是为了避免 5 条并发的场景)。
|
||||
@@ -269,12 +275,15 @@ export function useAnalysisMetrics({ sourceId, start, end, namespace, rankingEna
|
||||
if ((err as Error).name === 'AbortError') return
|
||||
setError(err instanceof Error ? err.message : 'metrics failed')
|
||||
} finally {
|
||||
if (version === versionRef.current) setLoading(false)
|
||||
if (version === versionRef.current) {
|
||||
setLoading(false)
|
||||
setAppsLoading(false)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
return () => { controller.abort() }
|
||||
}, [sourceId, start, end, namespace, appLabel, refreshTick, rankingEnabled])
|
||||
|
||||
return { data, loading, error, appLabel, refresh }
|
||||
return { data, loading, appsLoading, error, appLabel, refresh }
|
||||
}
|
||||
@@ -144,7 +144,7 @@ const AnalysisPage = memo(function AnalysisPage() {
|
||||
// 都依赖 by-app 数据,而它原来被底部排行卡片的可见性门控 ——
|
||||
// 用户不滚到底就永远拿不到数据,表现为"三条都没有应用维度的数据"。
|
||||
// 现在进页面就发,页面用两阶段渲染:索引类指标先出,app 维度到货后补。
|
||||
const { data, loading, error, appLabel, refresh } = useAnalysisMetrics({
|
||||
const { data, loading, appsLoading, error, appLabel, refresh } = useAnalysisMetrics({
|
||||
sourceId: effectiveSourceId,
|
||||
start,
|
||||
end,
|
||||
@@ -170,7 +170,9 @@ const AnalysisPage = memo(function AnalysisPage() {
|
||||
const daily = useDailyVolume({
|
||||
sourceId: effectiveSourceId,
|
||||
days: dailyDays,
|
||||
enabled: dailyView.inView,
|
||||
// 不再按可见性懒加载:这个接口只读平台库,实测 9ms。
|
||||
// 之前门控 + ref 观察器导致"滚到底也不加载",看起来就是"每日日志量一直没数据"。
|
||||
enabled: Boolean(effectiveSourceId),
|
||||
})
|
||||
|
||||
// 非 admin:菜单不可见,直访时给明确提示(后端 AdminAuth 403 兜底)
|
||||
@@ -248,7 +250,14 @@ const AnalysisPage = memo(function AnalysisPage() {
|
||||
<ErrorState message={error} onRetry={refresh} />
|
||||
) : loading && !data ? (
|
||||
<LoadingSpinner className="h-64" />
|
||||
) : !data || !sourceId ? null : (
|
||||
) : !data || !sourceId ? (
|
||||
/* data 为 null 时原来直接 return null → 整页空白,用户以为"没数据"。
|
||||
这里明确显示"正在统计"而不是什么都不画。 */
|
||||
<div className="flex h-64 flex-col items-center justify-center gap-2 text-sm text-muted-foreground">
|
||||
<LoadingSpinner />
|
||||
<p>{t('table.computing')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* 不再显示"告警行数"卡片:为了把它从 3 条重查询降到 2 条,
|
||||
已放弃全局 warn 统计。显示一个恒为 0 的数字比不显示更误导。 */}
|
||||
@@ -298,7 +307,7 @@ const AnalysisPage = memo(function AnalysisPage() {
|
||||
{/* 副标题说明这块不需要读日志内容,避免和上面的排行混淆 */}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loading && !data.apps.length ? (
|
||||
{(appsLoading || loading) && !data.apps.length ? (
|
||||
// 这块必须按 app 分组统计(读日志内容),实测大集群 15 分钟约 20~30 秒。
|
||||
// 明确告诉用户在算什么、要等多久,比空表格或整页转圈好。
|
||||
<div className="flex h-40 flex-col items-center justify-center gap-2 text-sm text-muted-foreground">
|
||||
|
||||
Reference in new issue
Block a user