Files
tamabox/.workbuddy/memory/2026-10-24.md
T
tamakyi 0c282914c2 fix(stats): 今日计数不再被每日缓存冻结,日期分组不依赖库会话时区
自查部署结果时发现两处正确性问题,都是本页上线时引入的:

1. 「今日提问 / 今日回答」原本随整页聚合一整天缓存,页面却写着「实时」,
   实际当天第一次访问后数字就冻住到次日。现拆出 TodayCounts 单独取,
   60 秒 TTL、跨日按日期失效;命中整页缓存时返回副本,不原地改缓存指针
   (那会被并发读踩成数据竞争)。

2. 日/月/时分组用 to_char/EXTRACT 直接作用在 timestamptz 上,结果跟着数据库
   会话时区走,而 PG 官方镜像默认 UTC、Go 侧按容器时区(Asia/Shanghai)切今天。
   实测同一条 2026-10-24 01:30+08 的提问,UTC 会话下被算成 10-23 17 点 ——
   日报表整体偏 8 小时、热力图时段错位,且与「今日」对不上。
   现统一经 analyticsLocalize() 加 AT TIME ZONE INTERVAL 换算,偏移取自
   time.Time.Zone()(Go 未设 TZ 时给 "Local",PG 不认该名,故不用 IANA 名)。
   过滤条件仍是裸列,索引照用。

另外两处顺手修:

- PeakDay 用 >= 比较,并列时会漂到最近的同值日期,与 busiestSlot 的取值规则不一致。
- 聚合改用 context.WithoutCancel + 30s 超时:第一个访客在聚合途中关掉页面时,
  排在后面等锁的请求不会各自重跑一遍全表 GROUP BY。

测试:新增 TestTodayNumbersNotFrozen(缓存命中后今日数仍更新、整页聚合当天只算一次)
与 TestTodayCacheExpiresAcrossMidnight;analytics 连真库通过。
2026-10-24 01:20:02 +08:00

50 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-10-24
## /stats 入口收敛到页脚
- 用户要求:首页那张统计入口卡取消,入口只放页脚「社区规则」与「找站长」之间。
- 改动:
- `templates/home.html`:删掉统计入口卡整块 + `.tb-stats-entry*` 样式(原 `</details>` 后那一段)。
- `route/home.go`:`Home` 不再调 `stats.Get`(模板已不读 `.SiteStats`),去掉 `logrus` import;
`/api/v1/site-stats` 与 `SiteStatsAPI` 保留未动。
- 四份 i18n 字典删掉已无引用的 `site.stats.open`(`site.stats` 老词条保留)。
- 页脚 `templates/base/footer.html` 的 `site.footer.stats` 位置本就在 rules 与 admin 之间,无需再动。
- 验证:`go build` + `./internal/i18n` / `./templates` / `./route/...` 测试全绿;gofmt 无差异;
重新生成预览确认首页无 `tb-stats-entry`,页脚渲染为「社区规则 | 数据 | 找站长」。
- 教训:删掉页面唯一消费方后,服务端的取数调用(`stats.Get`)要同步删,
否则每次访问首页白算一次快照;对应 i18n 词条也要一起清,避免留死条目。
## /stats 文案与脚注排版修正
- 页脚入口文案「数据」→「统计数据」(`site.footer.stats`,四语言 `Statistics` / `統計データ` / `統計數據`)。
- **「提问最集中在三 19 点前后」的 bug**:洞察句子复用了热力图列头的 `stats.wd.*`
("一"…"日" / "Mon"…),那是为窄列头设计的单字标签,进句子就成了"三"、英文是 "Wed"。
新增一组完整名 `stats.weekday.1..7`(周一/週一/Monday/月曜日),洞察改用这组;
`stats.wd.*` 只留给热力图列头。已在 `overview_test.go` 加断言锁住,防止回退。
→ 教训:**短标签和完整名必须分两套词条**,别为了少写一组 key 复用。
- 页面脚注四条时间(基准日 / 计算于 / 最后一条提问 / 最后一条回答)由
「A:x · B:y」挤成一行改为 `grid-template-columns: max-content 1fr` 各占一行,
标签列与值列对齐;下面两段说明仍为独立段落。四个标签统一带冒号。
## 部署前自查:两个 P0 都已修
用户部署到测试环境后要求自查。查出的问题都是我自己上一轮引入的:
1. **「今日」两项被每日缓存冻住**(P0,与页面「实时」说明矛盾)。
→ `TodayCounts` 拆出,60 秒 TTL + 跨日按日期失效;命中缓存时返回副本。
新增 `TestTodayNumbersNotFrozen` / `TestTodayCacheExpiresAcrossMidnight`。
2. **日期分组跟着 PG 会话时区(默认 UTC)**(P0,隐蔽:数字都"合理",只是整体偏 8 小时)。
psql 实测:`2026-10-24 01:30+08` 在 UTC 会话下 to_char 得 `2026-10-23`、hour=17。
→ `analyticsLocalize()` 用 `AT TIME ZONE INTERVAL '+08:00'`(偏移取 `time.At()` 的 Zone,
不用 IANA 名)。EXPLAIN 确认过滤仍走 created_at 索引。
3. PeakDay 用 `>=` 会让「最忙的一天」随更新漂移到最近的同值日期 → 改 `>`。
4. `ovRefresh` 里的聚合改用 `context.WithoutCancel` + 30s 超时:第一个访客中途关页面时,
排队的请求不会各自重跑全表 GROUP BY。
性能实测(20 万行):totals 337ms / 字数分布 313ms / 热力图 100ms / 日趋势 47ms,
本地合计约 0.8s,1C1G 估 2s,靠预热兜住。百万行才需要合并扫描。
顺带发现(未改,与本次无关):`Dockerfile` 基础镜像是 `golang:1.20-alpine`,
而 `go.mod` 要求 `go 1.27.3` —— 1.20 不支持 GOTOOLCHAIN 自动下载,Docker 构建必然失败;
`-X` 里的包路径还是 `NekoWheel/NekoBox`。说明实际部署不走这个 Dockerfile。