feat: implement rsync delete timing (--delete-before/--delete-during/--delete-delay/--delete-after)
Deletion timing is now real and selected by the four rsync flags plus the plain --delete default. Wire protocol bumps to 2.8.0: two new config booleans (delete_during, delete_delay) are serialized and validated, joining the existing delete_before/delete_after. - Early modes (--delete-before, --delete-during/--del): the sender pre-scans the whole tree (paths only), transmits the keep-set manifest BEFORE any file data, and the receiver removes extras and acks STATUS_OK; the sender only streams data after the deletion committed. Deletion is thus performed even if a later transfer phase fails (rsync delete-before/during are destructive by definition). FastSync streams in a single scan so it cannot interleave per-directory like rsync delete-during; --delete-during selects the same engine mode as --delete-before (documented divergence). - Late/commit modes (plain --delete, --delete-after, --delete-delay): the manifest closes the data stream and deletion is committed only after STATUS_FINISHED proves the whole transfer succeeded, preserving FastSync's commit-style safety. --delete-delay converges with --delete-after because FastSync never snapshots the destination during data flow (documented). - The STATUS_MANIFEST frame is now self-delimiting and position-independent. Single-threaded receivers delete before the success frame; the -m receiver hands the keep-set to server.c, which commits the deletion only after the disk writer thread has drained (fixes a delete-vs-in-flight-temp race). - Every timing flag implies --delete; at most one timing flag is allowed. - Each timing flag implies --delete, matching rsync; conflicts are rejected.
This commit is contained in:
@@ -27,6 +27,7 @@ PipelineContextSender* pipeline_context_sender_create(Config* config, Queue* que
|
||||
context->loader_done = false;
|
||||
context->manifest = NULL;
|
||||
context->remove_source_files = NULL;
|
||||
context->early_delete = false;
|
||||
context->total_files = 0;
|
||||
context->progress_bytes = 0;
|
||||
context->total_bytes = 0;
|
||||
@@ -113,6 +114,7 @@ PipelineContextReceiver* pipeline_context_receiver_create(Config* config, Queue*
|
||||
context->receiver_done = false;
|
||||
context->queued_bytes = 0;
|
||||
context->max_queue_bytes = 0;
|
||||
context->deferred_manifest = NULL;
|
||||
atomic_init(&context->cancelled, false);
|
||||
int init = 0;
|
||||
if (mtx_init(&context->mutex, mtx_plain) != thrd_success)
|
||||
@@ -141,6 +143,8 @@ fail:
|
||||
|
||||
void pipeline_context_receiver_destroy(PipelineContextReceiver* context) {
|
||||
config_delete(context->config);
|
||||
if (context->deferred_manifest)
|
||||
array_list_delete(context->deferred_manifest);
|
||||
queue_destroy(context->queue);
|
||||
receiver_outcomes_destroy(&context->outcomes);
|
||||
mtx_destroy(&context->mutex);
|
||||
@@ -236,7 +240,8 @@ int receive_thread(void* pipeline_context) {
|
||||
mtx_unlock(&context->mutex);
|
||||
|
||||
ReceiverSink sink = {receiver_enqueue_file, context, false, false, NULL};
|
||||
if (receiver_process((Config*)config, file_descriptor, &sink) != 0) {
|
||||
if (receiver_process_pending((Config*)config, file_descriptor, &sink,
|
||||
&context->deferred_manifest) != 0) {
|
||||
receiver_thread_fail(context);
|
||||
protocol_session_unbind();
|
||||
return thrd_error;
|
||||
|
||||
Reference in New Issue
Block a user