docs(parity): correct delete-delay budget, fuzzy, and test-review gaps
- --delete-delay: state that the reported count advances on actual removal while --max-delete is charged at plan/snapshot time (defer_add/planned). A new differential shows rsync instead charges on actual removals and recursively removes a queued directory, so the row moves to Caveat (matrix 111/13/33) and the residual is pinned by tests. - Add a deterministic unit test (plan-time budget charge), a FastSync integration test (byte-barrier refill + --max-delete), and an rsync differential for a refilled deferred directory. - Soften the --fuzzy summary: the tree is byte-exact by design, so it is pinned by the threshold suite, not a byte-level differential. - README: describe what -m parity actually selects; fix the allowlist example to the real max_delete entry. - xdist-safe delete-timing fixture names (timing and --threads mode). - Renumber the duplicate HANDOFF item 9 to 10; add the missing final newline to test_checksum.c. - Expose ignore_errors_allows_delete and unit-test the deletion gate without a privileged source directory; update the stale deleted-count doc comment. No production behavior changes.
This commit is contained in:
@@ -149,8 +149,77 @@ static void test_delete_delay_max_delete_bounds_actual(void) {
|
||||
config_delete(config);
|
||||
}
|
||||
|
||||
/* --max-delete is charged at plan/snapshot time, not at actual removal: a
|
||||
* deferred entry that survives ENOTEMPTY still consumes its budget slot, so a
|
||||
* later directory's extra is skipped even though nothing was actually removed.
|
||||
* The reported count stays 0 (actual removals) while the run is partial. The
|
||||
* two plans are sent as separate frames for "a" then "b", so the ordering that
|
||||
* decides which entry gets the budget is deterministic (unlike readdir order). */
|
||||
static void test_delete_delay_refilled_dir_charges_budget_at_plan(void) {
|
||||
char root[] = "/tmp/fastsync_dp_planbudget_XXXXXX";
|
||||
EXPECT_TRUE(mkdtemp(root) != NULL);
|
||||
char adir[1024], bdir[1024], xdir[1024], ydir[1024];
|
||||
snprintf(adir, sizeof(adir), "%s/a", root);
|
||||
snprintf(bdir, sizeof(bdir), "%s/b", root);
|
||||
snprintf(xdir, sizeof(xdir), "%s/a/x", root);
|
||||
snprintf(ydir, sizeof(ydir), "%s/b/y", root);
|
||||
EXPECT_EQ_INT(mkdir(adir, 0700), 0);
|
||||
EXPECT_EQ_INT(mkdir(bdir, 0700), 0);
|
||||
EXPECT_EQ_INT(mkdir(xdir, 0700), 0);
|
||||
EXPECT_EQ_INT(mkdir(ydir, 0700), 0);
|
||||
|
||||
Config* config = config_create();
|
||||
EXPECT_NOT_NULL(config);
|
||||
config->receive_root_directory = str_dup(root);
|
||||
config->use_delete = true;
|
||||
config->delete_delay = true;
|
||||
config->max_delete = 1;
|
||||
|
||||
int p[2];
|
||||
EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0);
|
||||
|
||||
DeletePlanSession* session = delete_plan_session_create(config);
|
||||
EXPECT_NOT_NULL(session);
|
||||
/* Plan "a" first: its empty extra dir snapshots and charges the budget. */
|
||||
send_plan_frame(p[1], "a");
|
||||
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
||||
EXPECT_FALSE(delete_plan_session_limit_reached(session));
|
||||
/* Plan "b": the budget is already spent at snapshot time, so b/y is skipped
|
||||
even though a/x has not (and will not) be removed. */
|
||||
send_plan_frame(p[1], "b");
|
||||
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
||||
EXPECT_TRUE(delete_plan_session_limit_reached(session));
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
||||
|
||||
/* Refill a/x so its deferred rmdir fails ENOTEMPTY. */
|
||||
char refill[1200];
|
||||
snprintf(refill, sizeof(refill), "%s/new.txt", xdir);
|
||||
int fd = open(refill, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
EXPECT_TRUE(fd >= 0);
|
||||
close(fd);
|
||||
|
||||
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_LIMIT_REACHED);
|
||||
/* Nothing was actually removed, and the plan-time budget still stopped b/y. */
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
||||
struct stat st;
|
||||
EXPECT_EQ_INT(lstat(xdir, &st), 0);
|
||||
EXPECT_EQ_INT(lstat(ydir, &st), 0);
|
||||
|
||||
delete_plan_session_destroy(session);
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
unlink(refill);
|
||||
rmdir(xdir);
|
||||
rmdir(ydir);
|
||||
rmdir(adir);
|
||||
rmdir(bdir);
|
||||
rmdir(root);
|
||||
config_delete(config);
|
||||
}
|
||||
|
||||
void test_delete_plan(void) {
|
||||
test_delete_delay_refilled_dir_not_counted();
|
||||
test_delete_delay_removed_file_counted();
|
||||
test_delete_delay_max_delete_bounds_actual();
|
||||
test_delete_delay_refilled_dir_charges_budget_at_plan();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user