aw-sync: `daemon` (the default subcommand) never pulls — two incompatible sync-folder layouts
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 58/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- rust
調査の方向性
Start in aw-sync’s Commands::Daemon and Commands::Sync paths, then read sync_wrapper::pull_all, sync_wrapper::push, sync::sync_run, and the walkers in aw-sync/src/util.rs. Compare find_remotes() with get_remotes() and check when setup_local_remote() runs for pull mode. Done means daemon and sync/Android use one readable layout, remote databases are found, and pull-only cycles do not create local staging files.
索引モデルが issue の本文から書いたものです。
説明
aw-sync ships two mutually incompatible sync-folder layouts, and the one used by the default daemon subcommand can never see remotes written by the other one.
Net effect: running bare aw-sync (which is what the bundled binary and aw-qt do) is push-only, into a directory nothing else reads. It silently never pulls.
The two layouts
| code path | used by | writes | scans |
|---|---|---|---|
sync_wrapper::push / pull |
aw-sync sync (no advanced flags), aw-android (syncPush/syncPullAll/syncBoth via JNI) |
{sync_dir}/{hostname}/{device_id}/test.db |
{hostname}/{device_id}/*.db (3 levels) |
sync::sync_run called directly |
aw-sync daemon — i.e. the default subcommand |
{sync_dir}/{device_id}/test.db |
{sync_dir}/*/*.db (2 levels) |
find_remotes() in aw-sync/src/util.rs walks exactly two levels:
fs::read_dir(sync_directory)?
.filter(|p| p.is_dir()) // {sync_dir}/{hostname}
.flat_map(|d| fs::read_dir(d).unwrap()) // {sync_dir}/{hostname}/{device_id} <- a dir, not a .db
.filter(|path| path.extension() == "db")
Real peer databases sit one level deeper, so the filter drops every one of them. find_remotes_nonlocal() returns an empty vec, and the pull loop in sync_run() iterates over nothing.
Meanwhile setup_local_remote(sync_spec.path, device_id) stages the local push at {sync_dir}/{device_id}/test.db — at the folder root, with no hostname level — where get_remotes() (which requires a subdirectory containing a .db) will not find it either. So a daemon-mode device is invisible to aw-sync sync peers and to Android, in both directions.
Reproduction / evidence
Setup: macOS desktop running the bundled aw-sync with no arguments (→ daemon), sync folder shared over Syncthing with an Android device on v0.14.2b1.
~/ActivityWatchSync/ after the Android device pushed successfully:
POCO F8 Ultra/41662faa-.../test.db 9.4 MB <- 3-level, from aw-android
poco_f8_ultra/41662faa-.../test.db 273 MB <- 3-level, from aw-android
erb-m2.localdomain/d7bc68e7-.../test.db
d7bc68e7-.../test.db 1.19 GB <- 2-level, from the local daemon
102 consecutive daemon sync passes, ~/Library/Logs/activitywatch/aw-sync/aw-sync_2026-09-14T21-45-45+0200.log:
102 Pulling...
102 Pushing...
0 Found N remote db files <- never logged, not once
The Android buckets never appear locally. The only reason this is not more widely reported is that the failure is completely silent — Pulling... is logged unconditionally before the (empty) loop.
Two further consequences on the same machine:
- The local host-layout staging copy at
erb-m2.localdomain/d7bc68e7-.../test.dblast received an event on 2025-01-22. Everything since has gone into the root-level 2-level db, so this desktop has not published anything its peers can read for ~8 months. - That root db is 1.19 GB and Syncthing replicates it to every device, where nothing reads it. It also still contains
…-synced-from-…buckets (re-exported peer data) predating #648 — that fix stopped new ones being created but nothing cleans up existing ones.
Suggested fix
Collapse to one layout. Cheapest correct change: have Commands::Daemon call sync_wrapper::pull_all + sync_wrapper::push per cycle, exactly as the Commands::Sync fallback branch does, instead of driving sync::sync_run against the sync root. That makes the daemon agree with aw-sync sync and with Android, and leaves sync_run as the per-directory primitive it already is.
If the 2-level {device_id}/ scheme is preferred long-term (it is arguably the better one — see the identity discussion in the linked issues), then it needs a migration for existing folders plus a matching change in aw-android, and get_remotes()/find_remotes() must be unified rather than left as two different walkers.
Either way find_remotes() and get_remotes() should share one implementation — the TODO: share logic with find_remotes and find_remotes_nonlocal already in the source is precisely this bug.
Related, same area
sync_wrapper::pull() calls sync_run with sync_spec.path = {sync_dir}/{host}, and sync_run unconditionally calls setup_local_remote(path, device_id) — including in SyncMode::Pull. So pulling from a peer creates an empty {sync_dir}/{peer_host}/{my_device_id}/test.db inside that peer's folder. On the machine above this produced a d7bc68e7-… directory inside erb-laptop2-arch/, tekla-air-m1/, erb-pn50/, steamdeck/ and others.
This breaks the "each device only writes to files in the sync folder they own" invariant the whole design rests on (stated in aw-sync/README.md), and is a plausible source of the test.sync-conflict-*.db files that show up in these folders. setup_local_remote should only run when the mode actually pushes.
cc @TimeToBuildBob
- 主要言語
- Rust
- スター
- 315
- フォーク
- 97
- 平均マージ
- 1日 10時間
- マージ済み PR(30日)
- 61
環境構築
このプロジェクトの環境構築ファイルはまだ確認していません。まず 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
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
linebender/parley#849 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
boxlite-ai/boxlite#1729 ·
メンテナーはふだん 1 日以内に返信
-
external
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
langchain-ai/docs#6255 ·
メンテナーはふだん 1 日以内に返信
-
P2 testing
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
メンテナーはふだん 1 日以内に返信