Repository navigation
fix(sync): keep placeholder categories from overwriting real ones - #3
Merged
AritxOnly merged 2 commits intoNov 3, 2026
Merged
Conversation
A snapshot export read categories and children in separate queries outside a transaction, so a concurrent local write could export a habit or task whose category was missing. The receiving device then inserted the "未命名分类" placeholder with the child's timestamp; export derived an unstored version from that timestamp, so the placeholder outranked the real category and renamed it on every device. - Export snapshots in one transaction for a consistent read. - Give placeholder categories the epoch timestamp so any real version wins. - Add a regression test with a torn snapshot.
AritxOnly
approved these changes
Nov 3, 2026
AritxOnly
left a comment
Contributor
There was a problem hiding this comment.
Fixed the preset collision by giving new placeholder categories a timestamp earlier than the built-in epoch presets. Added a regression covering a missing preset, a higher placeholder device ID, and repair from the real category.
Validation: combined PR #1–#4 checkout with both follow-up fixes; ./gradlew --offline -PlifiMode=stub :shared:macosArm64Test — 280 tests passed, 0 failures. Private Lifi mode and iOS framework linking were not rerun in this pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
现象
两台设备同步时,一台设备新建分类后马上新建使用它的习惯(或任务),有一定概率这个分类在所有设备和云端都变成「未命名分类」(灰色文件夹),原来的名称、图标、颜色丢失。Mac 客户端的双设备同步测试大约每 15~30 次复现一次。
原因
两处叠加:
导出快照不是一致读取
SnapshotV3Store.export()依次查询分类、任务、专注、习惯……没有放在同一个事务里。占位分类的版本比真实分类新
upsertHabit→ensureCategoryReference插入「未命名分类」,时间戳取习惯的updatedAt。export()时versionFor(CATEGORY, …)用这个时间戳生成版本,比真实分类的新。TaskRepository)。改动(3 个文件,+51/-3)
SnapshotV3Store.export():包在transactionWithResult中,保证一致读取。原函数体改名为私有的exportInTransaction(),内容不变。ensureCategoryReference:占位分类的createdAt/updatedAt使用1970-01-01T00:00:00Z,任何真实版本都能覆盖它。函数签名不变,调用方无需修改。DatabaseIntegrationTest.placeholderCategoryFromIncompleteSnapshotNeverOverwritesRealCategory:验证
Expected <Real>, actual <未命名分类>),改动后通过。./gradlew -PlifiMode=stub :shared:macosArm64Test:275 个测试全部通过。./gradlew -PlifiMode=private :shared:macosArm64Test:329 个测试全部通过。不影响
export()签名不变,新增的函数和常量都是 private / internal)。