Skip to content

Commit d1ebcdd

Browse files
committed
sync: preserve local archive intent across reconcile
1 parent 200b27e commit d1ebcdd

4 files changed

Lines changed: 747 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ EXPERIMENTAL, so on-disk formats and the CLI may change without notice.
2828

2929
### Fixed
3030

31+
- **Bus archive/delete intent lost to an inbound reconcile.** An atomic local
32+
rename could remove an inbox file while Fabric's filesystem watcher was still
33+
inside its 150 ms debounce window; an inbound sync in that gap could
34+
materialize the stale Present entry and undo the archive. Inbound sessions now
35+
carry an explicit last-observed disk receipt across reconciliation, durably
36+
record local changes before merging, and guard final materialization. This
37+
preserves deletes in both pre-merge and post-scan races while still accepting
38+
genuinely new remote files, without deadlocking simultaneous peer syncs.
39+
3140
- **Peer-config split when the daemon runs with `--home <default-root>`.** The
3241
managed service always launches the daemon as
3342
`--home ~/.local/share/fabric`, which made it read `peers.toml` from under that

src/daemon.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,17 +2532,21 @@ async fn handle_sync(connection: Connection, state: Arc<DaemonState>) -> Result<
25322532
let resolver_engine = engine.clone();
25332533
let outcome = sync::wire::run_server(stream, move |name| {
25342534
let engine = resolver_engine.clone();
2535-
async move { engine.node_for(&name).await }
2535+
async move {
2536+
let prepared = engine.prepare_inbound(&name).await?;
2537+
Ok(prepared.map(|prepared| (prepared.node(), prepared)))
2538+
}
25362539
})
25372540
.await;
25382541
match outcome {
2539-
Ok((name, stats)) => {
2542+
Ok((name, stats, prepared)) => {
25402543
if !stats.is_noop() {
25412544
debug!(sync = %name, ?stats, "served sync reconcile");
25422545
}
2543-
// Persist and write what the peer pushed to disk.
2544-
if let Err(error) = engine.materialize_entry(&name).await {
2545-
debug!(sync = %name, %error, "sync materialize failed");
2546+
// Re-scan changes that landed during the session, then persist and
2547+
// materialize while the inbound operation guard is still held.
2548+
if let Err(error) = engine.complete_inbound(prepared).await {
2549+
debug!(sync = %name, %error, "sync completion failed");
25462550
}
25472551
}
25482552
Err(error) => debug!(%error, "sync serve failed"),

0 commit comments

Comments
 (0)