fix(share): 单题卡缓存 key 补 user.Domain;纠正过期的 TTL 注释

先查了「缓存 key 漏项」到底有没有实际影响,结论是**当前没有**:

- 用户域名只在注册时写入(register.go / CreateUser),没有改域名的入口,
  所以两张卡的二维码内容不会在运行期变化
- ExternalURL / ShareBackground 来自配置,改了必须重启,而缓存是进程内的,
  重启即清空 —— 不存在「配置变了缓存还在」的窗口
- 头像、背景上传走 R2 随机 key(picture/日期/随机 hex,内容不可变),
  换图必然换 URL,key 随之变化,不会出现「同名不同图」

只有一条理论缺口:域名要是将来能被修改,单题卡 key 没算 user.Domain,
会出现最长一小时的「二维码指向旧域名」。补上这一项成本为零,先按事实算进去。
shareCardCacheKey 本来就含 Domain,两张卡现在一致。

顺带把两处「TTL 30 分钟」的注释改成 1 小时(代码一直是 time.Hour)。
新增 TestShareCardCacheKeysCoverDrawnFields:逐个改动画进图里的字段,
断言 key 必须随之变化;去掉 user.Domain 后立刻报
"user.Domain is drawn on the question card but not in questionShareCardCacheKey"。
This commit is contained in:
tamakyi
2026-10-23 00:58:31 +08:00
parent 8f849123f7
commit 675cecb21d
2 changed files with 62 additions and 4 deletions
+9 -4
View File
@@ -328,7 +328,7 @@ func (c *ttlCache) cleanup() {
// 社交图标 URL 固定不变,基本永远命中;头像/背景变更时最多延迟 1 小时生效。
var imageCache = newTTLCache(24)
// 分享图结果缓存,TTL 30 分钟。key 含用户资料指纹,资料变更后自动失效。
// 分享图结果缓存,TTL 1 小时。key 含用户资料指纹,资料变更后自动失效。
// 存的是 PNG 编码字节(约 0.2~1MB/张),限制 32 条,总内存 ≤ 32MB。
var shareCardCache = newTTLCache(32)
@@ -561,7 +561,9 @@ func writeShareCardResponse(ctx context.Context, data []byte) {
}
// shareCardCacheKey 基于用户资料生成缓存指纹。
// 资料(名称/简介/头像/背景/社交链接)任何一项变更都会导致 key 变化,缓存自动失效。
// 资料(域名/名称/简介/头像/背景/社交链接)任何一项变更都会导致 key 变化,缓存自动失效。
// conf 里的 ExternalURL / ShareBackground 不入 key:改配置必须重启,进程内缓存随之清空。
// 头像、背景走 R2 随机 key(内容不可变),换图即换 URL,也不会出现同名不同图。
func shareCardCacheKey(user *db.User) string {
h := md5.New()
fmt.Fprintf(h, "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s",
@@ -948,7 +950,7 @@ func drawQuestionQRCard(canvas *image.RGBA, user *db.User, question *db.Question
return nil
}
// 单题分享图结果缓存,TTL 30 分钟,上限 16 条(1C1G 内存友好)。
// 单题分享图结果缓存,TTL 1 小时,上限 16 条(1C1G 内存友好)。
var questionShareCache = newTTLCache(16)
// QuestionShareCard 生成单个问题(问题+回答)的分享卡片图。
@@ -1111,9 +1113,12 @@ func QuestionShareCard(ctx context.Context) {
}
// questionShareCardCacheKey 基于问题内容与用户资料生成缓存指纹,内容/资料变更自动失效。
// user.Domain 虽然当前不可改(只在注册时写入),但它进了二维码内容:一旦将来开放改域名,
// 漏掉它就会出现「卡片二维码指向旧域名」的一小时窗口,这里先按事实把它算进去。
func questionShareCardCacheKey(user *db.User, question *db.Question) string {
h := md5.New()
fmt.Fprintf(h, "q|%d|%s|%s|%s|%s|%s", question.ID, question.Content, question.Answer, user.Name, user.Avatar, user.Background)
fmt.Fprintf(h, "q|%d|%s|%s|%s|%s|%s|%s",
question.ID, question.Content, question.Answer, user.Domain, user.Name, user.Avatar, user.Background)
return fmt.Sprintf("qshare:%x", h.Sum(nil))
}