perf(api): 会话/5数字弹窗超时修复——日粒度汇总层 msg_daily_stats

根因(服务器实测):sessions 对 owner 全量 message GROUP BY,重账号 5.2万条
3~5s;staff-chat-stats 全时段窗 6.9s——128MB buffer pool 下被推过前端
15s 超时(ECONNABORTED)。EXPLAIN ANALYZE 证实最优 covering 索引也要
18.9s,瓶颈是磁盘不是查询形状。

修复(预聚合,云客同款思路):
- msg_daily_stats 表:每「渠道×账号×会话×上海自然日」一行(收发计数/
  当日最后时间与方向);迁移 a7d3e9f1c2b4(幂等建表)
- 写路径:repos.upsert_messages 落库自动重算本批触及的会话×天(所有
  抓取路径汇到该函数,一处挂全;失败不阻塞写入)
- 读路径:sessions/staff-chat-stats 表有行时读汇总(namedtuple 同形,
  端点主体零改动),空表守卫回退旧聚合(部署回填前不空白)
- 自愈:CLI daily-stats 全量重建(幂等);full_sync_all.sh 夜间追加第 11
  步——顺带修掉尾部 sqlite3 直连 smoke.db 的硬编码(服务器 MySQL 上会炸)
- 口径统一:day 一律 Asia/Shanghai Python 计算(旧 from_unixtime 跟随
  DB 会话时区与今日数割裂);统计窗口对齐自然日(文档化语义变化)

测试:12 新增(汇总层 8 + 端点判别/对拍 4,判别手法=回填后删光 message
仍出完整数据),全量 512 绿。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-03 16:37:04 +08:00
parent e3f7aa1304
commit 421998be54
11 changed files with 892 additions and 32 deletions
+18
View File
@@ -53,6 +53,7 @@ that one table. Multi-table routing is a LATER task (YAGNI for M1).
from __future__ import annotations
import logging
from typing import Any
import sqlalchemy as sa
@@ -74,6 +75,9 @@ from app.db.models import (
WechatAccount,
WhatsappAccount,
)
from app.storage.summary import recompute_days, touched_day_keys
_logger = logging.getLogger(__name__)
__all__ = [
"upsert_chat_group",
@@ -409,6 +413,20 @@ async def upsert_messages(session: AsyncSession, recs: list[dict[str, Any]]) ->
"""
for rec in recs:
await _upsert_one(session, Message, rec)
# 落库后维护日粒度汇总(msg_daily_stats):只重算本批触及的「会话×天」,
# 窄时间范围查询毫秒级。所有消息写入路径(页面抓取器微信/WA/群、CLI
# 手动同步)都汇到本函数,一处挂全。失败不阻塞消息写入——夜间
# recompute_all 自愈兜底(2026-09-03 会话列表超时修复)。
keys = touched_day_keys(recs)
if keys:
try:
await recompute_days(session, keys)
except Exception:
_logger.exception(
"daily-stats recompute failed (%d keys); nightly rebuild will heal", len(keys)
)
return len(recs)