Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

aw-sync writes to peer databases on every pull: WAL flip + schema migration on files it does not own

Aperta
#693 4 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
52/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
rust, sqlite

Direzione di ricerca

Start by tracing create_datastore in aw-sync and Datastore::new in aw-datastore/src/worker.rs, then read the compatibility TODO in sync.rs. Confirm how peer files are opened and migrated. Done means pulling a peer database performs no writes, journal or synchronous changes, migrations, or sidecar creation; incompatible versions are refused or handled through a scratch copy.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

The core invariant aw-sync's whole conflict-free story rests on — stated in aw-sync/README.md as "each device only writes to files in the sync folder they own, and other devices may not modify them" — is violated on every pull.

What happens

create_datastore opens a peer database read-write:

pub fn create_datastore(path: &Path) -> Result<Datastore, String> {
    ...
    Ok(Datastore::new(pathstr.to_string(), false))
}

and Datastore::new (aw-datastore/src/worker.rs) unconditionally:

let journal_mode: String = conn
    .pragma_update_and_check(None, "journal_mode", "WAL", |row| row.get(0))
    .expect("Failed to query journal_mode");
...
conn.pragma_update(None, "synchronous", "FULL")
...
let mut ds = DatastoreInstance::new(&conn, true).unwrap();

So reading a peer's file:

  1. Flips its journal_mode to WAL, creating -wal and -shm sidecars inside a directory owned by another device.
  2. Runs schema migrations on it — DatastoreInstance::new(&conn, true) upgrades user_version toward the local binary's version.
  3. Writes synchronous = FULL.

All three are writes to a file this device does not own, performed by a read-only operation, in a directory an external file syncer is actively replicating.

Why it matters concretely

  • Version skew makes it a real migration, not a no-op. Peer databases in a live sync folder sit at different user_versions, below current master's. An older peer's file gets silently upgraded by whichever device pulls it first — and that device may be running a newer aw-server-rust than the peer that owns the file. The owner then reopens a database migrated by someone else's binary.
  • It manufactures sync conflicts. Two devices pulling the same peer, or a pull racing the owner's push, are concurrent writers to one file. My folder already contains the fingerprints: erb-main3/5a5df0f8-…/test.sync-conflict-20241125-052022-GRUSU5T.db plus a second from the same day.
  • The -wal/-shm sidecars are themselves replicated, and a syncer that delivers a main file and its WAL at different moments produces exactly the torn snapshot the sidecars were meant to prevent.
  • It compounds #689's finding that pulls also create empty staging directories inside peers' folders. Between the two, a pull writes to a peer's directory in three different ways.

Fix

Open peer databases read-only and side-effect-free:

  • sqlite3_open_v2 with SQLITE_OPEN_READONLY, or a file:…?mode=ro URI
  • never run migrations on a file this device does not own — if user_version is unrecognised, refuse that peer and report it (there is already a // TODO: Check for compatible remote db version before opening in sync.rs), rather than upgrading it
  • do not set journal_mode or synchronous on peer files
  • if a read-only open is impractical for a WAL-mode peer, copy the file to a scratch location first and open the copy — never the original

Note today's Datastore::new also spawns a worker thread and min/max-scans every bucket on open, which is heavy for what a pull needs; a lightweight read-only open path would help #684's status command too.

Credit: found in a design review of aw-sync; the code path above is verified against origin/master, the conflict files are from a live sync folder.

Related: #689, #691 (sqlite as wire format), #682.

cc @TimeToBuildBob

Lingua principale
Rust
Stelle
317
Fork
99
Merge medio
1g 10h
PR unite (30g)
61

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di ActivityWatch/aw-server-rust

Tutte le issue di ActivityWatch/aw-server-rust

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.