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:
@@ -740,6 +740,46 @@ class SyncRun(Base):
|
||||
__table_args__ = (Index("ix_sync_run_entity_status", "entity_type", "status"),)
|
||||
|
||||
|
||||
class MsgDailyStats(Base):
|
||||
"""日粒度会话统计:每个「渠道×账号×会话×自然日」一行(2026-09-03)。
|
||||
|
||||
sessions(staff-chat 会话列表)与 staff-chat-stats(5 数字弹窗)的预聚
|
||||
合层——这两端点原先每次请求对 owner 全量 message 做 GROUP BY(重账号
|
||||
5.2 万条实测 3~7s,128MB buffer pool 下更被推过前端 15s 超时)。日行由
|
||||
写路径维护:``repos.upsert_messages`` 落库后自动重算本批触及的
|
||||
「会话×天」;夜间 ``recompute_all`` 全量重建自愈(裸 UPDATE/回填脚本
|
||||
等绕过钩子的写入由它兜底)。
|
||||
|
||||
口径:
|
||||
- ``day`` 一律 Asia/Shanghai 自然日(Python 计算,不用 SQL 日期函数——
|
||||
from_unixtime 跟随 MySQL 会话时区,UTC 容器下与今日数口径割裂)。
|
||||
- ``send_cnt``/``recv_cnt`` 仅计 is_send 真/假;NULL 两边都不计(对齐
|
||||
旧聚合的 case 语义)。
|
||||
- ``last_ts``/``last_is_send`` 为该日最后一条消息的时间与方向。
|
||||
"""
|
||||
|
||||
__tablename__ = "msg_daily_stats"
|
||||
|
||||
channel: Mapped[str] = mapped_column(String(16), primary_key=True)
|
||||
owner_account_id: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
# friend = 1:1 私聊(peer=talker_id) / room = 群(peer=room_id)
|
||||
peer_kind: Mapped[str] = mapped_column(String(8), primary_key=True)
|
||||
peer_id: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
# yyyymmdd,如 20260903(上海自然日)
|
||||
day: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
send_cnt: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0, server_default=sa.text("0")
|
||||
)
|
||||
recv_cnt: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0, server_default=sa.text("0")
|
||||
)
|
||||
# 当日最后一条消息(ms);当天无有效方向计数的消息也存在(is_send=NULL)
|
||||
last_ts: Mapped[int | None] = mapped_column(sa.BigInteger, nullable=True)
|
||||
last_is_send: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
||||
|
||||
updated_at: Mapped[dt.datetime] = _updated_at()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"ChatGroup",
|
||||
"Customer",
|
||||
@@ -748,6 +788,7 @@ __all__ = [
|
||||
"Media",
|
||||
"Message",
|
||||
"MomentsPost",
|
||||
"MsgDailyStats",
|
||||
"PhoneCall",
|
||||
"Sms",
|
||||
"Staff",
|
||||
|
||||
Reference in New Issue
Block a user