fix(imap): UIDVALIDITY 改为持久化随机值,修复数据库重建后客户端缓存永不失效(TB 只显示 1-3 封) #11

Merged
kevin merged 4 commits from dsh/mailgo:fix/imap-uidvalidity into main 2026-08-19 23:48:51 +08:00
Contributor

问题现象

  • Thunderbird 收件箱只显示 1-3 封邮件,每次刷新数量随机(1、3、1…)
  • 诊断日志显示 TB 只下载 UID 2、4、8、9 与 167:*,其余认为本地已有
  • 列表内容等于最近一次下载批次,本地缓存与实际邮箱内容不一致

根因

Status() 硬编码 UidValidity=1。数据库重建后消息 ID 空间完全变化(uid 2-166 对应完全不同的邮件),但 UIDVALIDITY 不变,RFC 3501 的缓存失效机制从未触发:Thunderbird 认为旧缓存仍然有效,只下载"新增"(uid 167:*)与"缺失"(2,4,8,9)的少数邮件。

修复

  • 新增 mailbox_states 表:每(用户,文件夹)首次访问随机生成并持久化 UIDVALIDITY(0 值修正,不同用户/文件夹独立)
  • Status() 返回持久化值;数据库重建后新值触发客户端丢弃缓存全量重同步
  • 补充 AutoMigrate 迁移(首版漏加导致回退 1)
  • 新增 TestMailboxStateUidValidity 单元测试

验证

  • 生产验证:SELECT 返回随机 UIDVALIDITY(如 669646605),persist 于 mailbox_states
  • TB 已检测到变化并触发一次全量 UID FETCH 1:*(61 封)
  • 遗留问题:TB 本地缓存已损坏(今日多次中断同步),需在 TB 中删除账户重新添加完成全量重建
## 问题现象 - Thunderbird 收件箱只显示 1-3 封邮件,每次刷新数量随机(1、3、1…) - 诊断日志显示 TB 只下载 UID 2、4、8、9 与 167:*,其余认为本地已有 - 列表内容等于最近一次下载批次,本地缓存与实际邮箱内容不一致 ## 根因 Status() 硬编码 UidValidity=1。数据库重建后消息 ID 空间完全变化(uid 2-166 对应完全不同的邮件),但 UIDVALIDITY 不变,RFC 3501 的缓存失效机制从未触发:Thunderbird 认为旧缓存仍然有效,只下载"新增"(uid 167:*)与"缺失"(2,4,8,9)的少数邮件。 ## 修复 - 新增 mailbox_states 表:每(用户,文件夹)首次访问随机生成并持久化 UIDVALIDITY(0 值修正,不同用户/文件夹独立) - Status() 返回持久化值;数据库重建后新值触发客户端丢弃缓存全量重同步 - 补充 AutoMigrate 迁移(首版漏加导致回退 1) - 新增 TestMailboxStateUidValidity 单元测试 ## 验证 - 生产验证:SELECT 返回随机 UIDVALIDITY(如 669646605),persist 于 mailbox_states - TB 已检测到变化并触发一次全量 UID FETCH 1:*(61 封) - 遗留问题:TB 本地缓存已损坏(今日多次中断同步),需在 TB 中删除账户重新添加完成全量重建
dsh added 2 commits 2026-08-19 21:59:45 +08:00
根因:Status() 硬编码 UidValidity=1。数据库重建后消息 ID 空间
完全变化(uid 2-166 对应完全不同的邮件),但 UIDVALIDITY 不变,
Thunderbird 认为本地缓存(旧 uid 1-166)仍然有效,只下载"新增"
(uid 167:*)与"缺失"(2,4,8,9)的少数邮件——列表只剩 1-3 封,
每次刷新数量随机。

修复(RFC 3501 UIDVALIDITY 语义):新增 mailbox_states 表,
每(用户,文件夹)首次访问随机生成并持久化 UIDVALIDITY;数据库
重建后新值触发客户端丢弃缓存全量重同步。0 值修正,不同用户/
文件夹互相独立。

新增 TestMailboxStateUidValidity 单元测试。
dsh added 1 commit 2026-08-19 22:21:20 +08:00
问题:ResetFail 只在 Web 登录调用,协议层失败计数只增不减。
合法用户的客户端(手机 APP 用裸用户名重试、配置向导探测、输错
密码等)失败次数持续累积,每达到阈值就触发一次封禁档位,从第 4
次触发起真实封禁 30 分钟+——用户被反复误封,手机端表现为一直
卡在"正在接收邮件"。

修复:
- IMAP/SMTP/POP3 认证成功路径调用 Bans.ResetFail(与 Web 一致)
- 新增 UserStore.AuthenticateLogin:支持裸用户名(如 "kevin"),
  唯一归属时自动解析到其域名;跨域名同名歧义时要求完整邮箱
- 新增 TestAuthenticateLoginBareUsername 单元测试
dsh added 1 commit 2026-08-19 22:35:51 +08:00
根因:date 列在 SQLite 中是文本,历史数据混存 +08:00/-04:00/+00:00
等时区偏移,ORDER BY date DESC 变成字符串字典序而非时间序:
- Web 列表最新邮件排不到最前(如 22:21+08 的邮件排在 20:03+08 后面)
- IMAP 序号与客户端按日期排序的视图不一致

修复:
- mailStore.Create 统一 msg.Date = msg.Date.UTC()(所有写入路径
  共用:SMTP/IMAP APPEND/Web/外发回执),新数据字典序即时间序
- 生产数据一次性迁移为 UTC 格式(130 条,Python 全量解析转换)
- 验证:kevin INBOX 排序 172→170→166→164…(严格最新在前)
kevin merged commit 7a394bbd9e into main 2026-08-19 23:48:51 +08:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kevin/mailgo#11