aw-sync writes to peer databases on every pull: WAL flip + schema migration on files it does not own
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
- Ambito
- databases, distributed-systems
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
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:
- Flips its
journal_modeto WAL, creating-waland-shmsidecars inside a directory owned by another device. - Runs schema migrations on it —
DatastoreInstance::new(&conn, true)upgradesuser_versiontoward the local binary's version. - 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.dbplus a second from the same day. - The
-wal/-shmsidecars 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_v2withSQLITE_OPEN_READONLY, or afile:…?mode=roURI- never run migrations on a file this device does not own — if
user_versionis unrecognised, refuse that peer and report it (there is already a// TODO: Check for compatible remote db version before openinginsync.rs), rather than upgrading it - do not set
journal_modeorsynchronouson 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
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ActivityWatch/aw-server-rust
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
ActivityWatch/aw-server-rust#763 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
ActivityWatch/aw-server-rust#724 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
ActivityWatch/aw-server-rust#717 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 68/100
ActivityWatch/aw-server-rust#714 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
aw-sync: no stored cursor — resume-from-destination silently never syncs late-arriving eventsApertabug
Difficoltà 5/5 Più di una settimana Idoneità per principianti 45/100
ActivityWatch/aw-server-rust#696 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di ActivityWatch/aw-server-rust
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
-
area: cli bug priority: P2 ready-for-agent
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno