fix(delay-updates): unique staging dir and implied --delete-after ordering (#317)

This commit is contained in:
2026-09-24 01:18:13 +02:00
parent f7d5dda93b
commit 96e02f52c0
14 changed files with 429 additions and 104 deletions
+18 -18
View File
@@ -981,12 +981,23 @@ static void server_run_mt_receiver(ServerSession* state) {
thrd_join(writer, &writer_result);
bool transfer_ok = receiver_result == thrd_success && writer_result == thrd_success;
PipelineContextReceiver* context = state->context;
if (transfer_ok && !config->dry_run) {
/* --delay-updates: receive_thread has finished the whole protocol stream
and write_thread has drained its queue, so every staged file is complete.
Publish atomically BEFORE the deferred delete commit, matching rsync's
--delete-after ordering (all updates land first, then extras are
removed). */
if (config->delay_updates && config->delay_context &&
!delay_updates_publish(config->delay_context, config)) {
transfer_ok = false;
}
}
if (transfer_ok && !config->dry_run) {
/* Commit-style (late) deletion: receive_thread handed the keep-set
manifest here instead of deleting while write_thread might still be
draining, so by now every file is on disk and the whole transfer is
known to have succeeded. Remove the extras before publishing a
--delay-updates run; the walker skips the staging directory. A
draining, so by now every file is on disk (and a --delay-updates run has
already published above) and the whole transfer is known to have
succeeded. The walker skips the staging directory. A
server-contacting --dry-run deletes nothing (no manifest is sent). */
if (context->deferred_manifest) {
size_t deleted = 0;
@@ -1026,21 +1037,10 @@ static void server_run_mt_receiver(ServerSession* state) {
delete_plan_session_destroy(context->deferred_plans);
context->deferred_plans = NULL;
}
}
if (transfer_ok && !config->dry_run) {
/* --delay-updates: receive_thread has finished the whole protocol stream
(including manifest/delete handling) and write_thread has drained its
queue, so every staged file is complete. Publish atomically before the
success/outcome frame so a --remove-source-files sender only learns of
files that were actually installed. */
if (config->delay_updates && config->delay_context &&
!delay_updates_publish(config->delay_context, config)) {
transfer_ok = false;
}
/* P7 Wave D: all writers have joined and the late deletion (and
--delay-updates publication) has committed above, so it is finally safe
to stamp directory times; a directory's mtime must not be clobbered by
its children or by an extra removal. */
/* P7 Wave D: all writers have joined and the --delay-updates publication
plus the late deletion have committed above, so it is finally safe to
stamp directory times; a directory's mtime must not be clobbered by its
children or by an extra removal. */
if (transfer_ok)
dir_metadata_list_apply(&context->dir_times, config->receive_root_directory, config);
}