aw-sync: duplicate folders for one device_id silently truncate history on pull
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 52/100
調査の方向性
Start with sync_one() in aw-sync/src/sync.rs and compare its resume logic with sync_wrapper::pull(), then trace get_or_create_sync_bucket() and the discovered remote folders. The fix is done when multiple folders for one device_id cannot silently truncate history, skipped sources are clearly warned about, and provenance or resume behavior is safe against hostname changes.
索引モデルが issue の本文から書いたものです。
説明
When one physical device has written under two different folder names in the sync directory, pull_all imports both. Because provenance is derived from the bucket's hostname field rather than the folder name, both folders resolve to the same destination bucket — and the resume logic then silently discards most of the history.
Whether you lose data depends on fs::read_dir ordering, which is arbitrary.
Mechanism
sync_one() (aw-sync/src/sync.rs) picks its resume point from the newest event already in the destination:
let resume_sync_at = most_recent_events
.first()
.map(|e| e.timestamp + e.duration)
.or(sync_spec.start);
and then only fetches events newer than that from the source. So if a folder holding a short, recent slice of history is imported before the folder holding the full history, the resume boundary jumps forward and everything older in the good database is never fetched. Re-running sync does not recover it — the boundary is now baked into the destination bucket. Only deleting the destination bucket and re-pulling recovers the data.
sync_wrapper::pull() already guards against this within a single folder:
if dbs.len() > 1 {
warn!("More than one db found in sync folder for host, choosing largest db {:?}", dbs);
}
but there is no equivalent check across folders, and nothing anywhere keys on device_id.
Real-world instance
One Android device, two folders, same device_id (41662faa-…), identical hostname on every bucket row inside both databases ("POCO F8 Ultra"):
| folder | size | aw-watcher-android events |
range |
|---|---|---|---|
POCO F8 Ultra/ |
9.4 MB | 4,068 | 2026-07-02 → 2026-07-18 |
poco_f8_ultra/ |
273 MB | 1,027,343 | 2021-05-18 → 2026-09-14 |
Both import into aw-watcher-android-synced-from-POCO F8 Ultra. If the 9.4 MB folder is walked first, the resume boundary becomes 2026-07-18 and the entire 2021–2026 history is skipped — ~1M events, silently, with ✓ Already up to date! in the log. aw-watcher-android-unlock (98,035 vs 100,188 events) truncates the same way.
The two folders exist because aw-android started sanitizing the device hostname (see the companion aw-android issue), but this is not Android-specific: any hostname change, re-install, or manual folder rename reproduces it.
Suggested fix
- Deduplicate by
device_idbefore importing. Group the discovered remotes by thedevice_idpath component; if a device_id appears under more than one folder, use one (largest, or newestlast_updated) andwarn!naming the folders that were skipped. This alone removes the hazard and would have caught the case above automatically. - Make
device_idthe provenance key, nothostname.get_or_create_sync_bucket()currently derives origin from$aw.sync.origin→ falling back tobucket_from.hostname; neither is stable across a rename, and both are decoupled from the folder the data actually came from. - Refuse to regress the resume boundary. If a source database's newest event is older than the destination's, that source has nothing to contribute — but if its oldest event is older than the destination's oldest, it holds history the destination lacks. Backfilling that case (or at minimum warning loudly about it) would make the operation safe regardless of ordering.
Discovered alongside the daemon layout bug filed separately; both were hit in the same setup.
cc @TimeToBuildBob
- 主要言語
- Rust
- スター
- 317
- フォーク
- 99
- 平均マージ
- 1日 10時間
- マージ済み PR(30日)
- 61
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
ActivityWatch/aw-server-rust のほかの issue
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
ActivityWatch/aw-server-rust#763 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
ActivityWatch/aw-server-rust#724 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
ActivityWatch/aw-server-rust#717 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 68/100
ActivityWatch/aw-server-rust#714 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 5/5 1週間以上 初心者へのやさしさ 45/100
ActivityWatch/aw-server-rust#696 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
ActivityWatch/aw-server-rust の issue をすべて見る
似ている issue
-
type/bug
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
stackabletech/kafka-operator#1033 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
bug good first issue needs testing
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 3 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
farion1231/cc-switch#7744 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
datafusion
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
apache/iceberg-rust#3297 ·
メンテナーはふだん 1 日以内に返信