Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

aw-sync: `daemon` (the default subcommand) never pulls — two incompatible sync-folder layouts

オープン
#682 コメント 7 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 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 の本文から書いたものです。

説明

bug

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.db last 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 を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

ActivityWatch/aw-server-rust のほかの issue

ActivityWatch/aw-server-rust の issue をすべて見る

似ている issue

Rust の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。