1.
在开发 Pencel 的时候,一直以来,我都是使用 Bundle id 来区别 Debug 和 Release,虽然半懂不懂,但:
- Bundle id 的区分碰巧能让本地的数据库隔离
- Debug 构建使用 development entitlement,即使是同一个 CloudKit container 也能走 development 环境
所以完全没问题。
今天鬼使神差,想通过独立的 Scheme 来做这件事,于是就拆成了 PencelmacOS 和 PencelmacOSDev 两个 Target。
PencelmacOSDev 仍然使用了独立的 Bundle id,PencelmacOS 则草率地沿用了 Release Bundle id (天真地以为开发环境下只要不编辑数据就行了),表面上看起来把 dev identity 显式定义得更清楚了,满足了我的强迫症。
2.
到这里的时候其实也无事发生,直到我 Build 了 PencelmacOS:生产环境和开发环境的本地缓存数据同时出现了 app 内。
原因是本地数据库是通过 Bundle id 来做路径上的隔离,所以此时相当于共用了数据库。
然后进一步的,由于 PencelmacOS 和 PencelmacOSDev 没有做 iCloud container 隔离,都指向 dev CloudKit,所以 PencelmacOSDev 的数据又被同步到了 PencelmacOS 下的 debug 构建。
出于本能,我马上在 PencelmacOS 的 debug 构建下删除了混入的 PencelmacOSDev app 数据,而此时 PencelmacOSDev 构建下的数据也因为同步行为而被清空。
所以在我此刻的视角下,PencelmacOSDev 的数据直接被「转移」到了 PencelmacOS 下,没有意识到是同步行为,令我非常困惑。
3.
不知所措的我又去 CloudKit 下把 Dev 环境 Reset 了。即使还不知道这意味着什么。
不过马上就知道了,PencelmacOS 的 debug app 下立刻出现了同步失败,原因是:
- 本地以为远端 record 还存在
- 本地保存着
CKRecordsystem fields /changeTag - 上行时仍走旧的 record 路径
- 但是远端为空,没有任何 record 所以无法上传
理论上,清掉 stale system fields 后,以原 UUID / recordName 重新 create / upsert,就能把本地数据重新发布到空的 dev 环境。但这件事涉及太多字段以及依赖顺序,手动触发并不可靠,所以最后让 Agent 补了「自愈」逻辑:
- 把这类变更重新排队或改成可重新创建的上行
- 给开发期 reset CloudKit 后恢复本地数据提供工具路径
4.
重新触发上传后,又暴露了一个设计缺陷:createdAt 没有作为业务字段同步到 CloudKit。
于是从云端重建本地库时,只能 fallback 到 CKRecord 的系统 creationDate;如果远端被清空后重新上传,这个时间就会变成重新创建 record 的时间。
好在还没有手贱到去 Reset 生产环境,现在补充了 createdAt 基本也不会带来风险:
- 下行对旧 record 缺
createdAt做了 fallback,不会 decode 失败 - 新字段只影响后续上行 record 更完整
题外话,修复过程中,又顺手修复了大量的文件夹软删除记录没被清理的问题,增加了自动清理 30 天以上已经被软删除的文件夹的 GC。
5.
再次为本地构建用的 PencelmacOS 补上了独立的 Bundle id,这让我之前的所作所为看起来是纯粹的没事找事,不过能暴露出问题并修复,也不算没意义。
但是事件还没结束,在我推送到 TF 后,尝试测试清库后的回填行为,不知为何只能回填部分文件,但同时利用 cktool query-records 又能确认远端确实没有丢数据。
更无语的是,大概率是因为 dev 环境的数据 / 依赖状态和 Production 不完全一致,所以这在 debug 下无法复现。
最后通过 Archive 的产物来监测回填时的行为才发现:Document 先于 Folder 落库,Document 带着 parentFolderId,但本地还没有对应 Folder,于是 apply 失败。
这又是一个旧的实现缺陷,搞清楚后修复也不困难:
- 下行顺序固定为
Folder→Asset→Document Folder内部还要确保父文件夹先于子文件夹Cold restoreapply failure 不能静默当成功- 针对回填后部分失败的状态需要
Repair,再跑一次全量恢复
这算是一个 P0 问题,虽然日常使用大概率不会出现清库的行为。除此以外我也只能祈祷用户 (如果有的话) 在跨设备全量同步时没有遇到这个情况。
6.
作为门外汉,数据库相关的问题相比前端总是更苦手。
除了祈祷 AI 变得更强以外,多学习一些哪怕是常识,也是一个长期课题。