Skip to content

perf(sync): reuse opened mirrors, barrier fsync, single-open staging - #71

Open
srnnkls wants to merge 3 commits into
mainfrom
perf/deploy-hot-path
Open

perf(sync): reuse opened mirrors, barrier fsync, single-open staging#71
srnnkls wants to merge 3 commits into
mainfrom
perf/deploy-hot-path

Conversation

@srnnkls

@srnnkls srnnkls commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Summary

Cold deploy, rebuild-registry, and sync --force spent most of their time re-opening the gix mirror per leaf and in F_FULLFSYNC. Three changes:

  • GitBackend keeps one ThreadSafeRepository per mirror and hands out to_thread_local() handles; the entry is forgotten when the mirror is refreshed or a worktree is recaptured. Before, every read ran gix::open and re-validated every pack index — 2,349 times per cold deploy.
  • Journal and registry write-then-rename used sync_all, which Rust maps to F_FULLFSYNC on Apple targets (5.7 ms each here, 4 per artifact). They only need ordering, so macOS now uses F_BARRIERFSYNC (0.7 ms) via sync::state::fsync_barrier; other targets keep sync_all. Durability on power loss is weaker; intent-before-swap ordering is unchanged.
  • Staged leaves are written, chmodded, and timestamped through one open handle instead of fs::write + a filetime reopen + metadata/set_permissions.

Measurements

arvato feat/phora target (94 artifacts, 2,349 leaves), hyperfine, macOS, disk 96 % full so cold-path variance is high:

path before after
cold deploy 5.95 s 1.79 s
rebuild-registry 4.44 s 1.30 s
sync --force 4.19 s 2.11 s
noop sync ~90 ms ~90 ms

CPU time (user+sys) drops 2.6× on every path; the remainder is APFS file creation and blob inflate.

Tests

  • reads_after_refresh_see_the_new_commit_and_the_old_snapshot pins cache invalidation across fetch and reclone.
  • barrier_succeeds_on_a_written_file covers the barrier call.
  • cargo test: 1,434 lib + 57 integration pass; clippy -D warnings and fmt clean.

Independent of #70 (different files); both branch from main.

Cold deploy re-opened the gix mirror and re-validated every pack index for each of
the 2,349 leaf reads; GitBackend now keeps one ThreadSafeRepository per mirror and
forgets it when the mirror is refreshed or a worktree is recaptured.

Journal and registry writes used sync_all, which is F_FULLFSYNC on macOS (5.7 ms
each here, 4 per artifact); write-then-rename only needs ordering, so they use
F_BARRIERFSYNC (0.7 ms).

Staged leaves are written, chmodded and timestamped through one open handle instead
of write + reopen for mtime + metadata/set_permissions.

arvato feat-phora target (94 artifacts, 2,349 leaves), hyperfine:
cold deploy 5.95 s -> 1.79 s, rebuild-registry 4.44 s -> 1.30 s, sync --force
4.19 s -> 2.11 s, noop unchanged (~90 ms).
Copilot AI lite review requested due to automatic review settings September 4, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new staging mtime path can panic on overflow (UNIX_EPOCH + Duration::from_secs(commit_time)), which should be converted back into a handled error as before.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR optimizes sync/deploy performance by reducing repeated Git mirror opens, lowering macOS fsync overhead during write-then-rename persistence, and avoiding extra file reopens when staging leaf outputs.

Changes:

  • Cache opened gix::ThreadSafeRepository instances per mirror in GitBackend, handing out thread-local repositories and invalidating the cache on refresh/recapture paths.
  • Introduce fsync_barrier to avoid F_FULLFSYNC overhead on macOS while preserving write ordering for atomic writes.
  • Write/chmod/timestamp staged leaf files through a single open file handle.
File summaries
File Description
src/sync/state/mod.rs Adds durable fsync module and re-exports fsync_barrier for state writers.
src/sync/state/file.rs Uses fsync_barrier in atomic state record writes to reduce macOS overhead.
src/sync/state/durable.rs Implements fsync_barrier (macOS barrier + fallback; other OS uses sync_all) and adds a unit test.
src/sync/journal.rs Uses fsync_barrier for journal temp-file durability step before rename.
src/sync/stage.rs Consolidates leaf staging into a single-open write_leaf flow including exec bit + mtime setting.
src/source/git.rs Caches opened mirrors in GitBackend and invalidates the cache on mirror refresh / worktree resolution paths.
src/source/mod.rs Adds regression test asserting correct cache invalidation across refresh and reclone scenarios.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/sync/stage.rs Outdated
file.set_permissions(perms)?;
}
}
file.set_modified(std::time::UNIX_EPOCH + std::time::Duration::from_secs(commit_time))?;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a986d07: checked_add and the original commit_time out of range error.

Comment thread src/sync/state/durable.rs Outdated
Comment on lines +4 to +6
/// Rust std maps `sync_all` to `F_FULLFSYNC` on Apple targets; write-then-rename
/// only needs the ordering `F_BARRIERFSYNC` gives.
#[cfg(target_os = "macos")]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a986d07: reworded to macOS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants