reidkaufmann opened a new pull request, #892: URL: https://github.com/apache/arrow-rs-object-store/pull/892
# Which issue does this PR close? - Part of #888. Builds on #890. Until #890 merges this PR also contains its two commits; the change here is the last commit, 6b1e655 ([diff](https://github.com/reidkaufmann/arrow-rs-object-store/compare/local-durable-dir-creation...local-unsynced-after-failed-creation)). # Rationale for this change The third interleaving in the issue: a creation's directory fsyncs can fail after it has created the directories (`File::open` on the directory with EMFILE, for example). Writes waiting on the creation are released and later writes find nothing registered, so they fsync only their own parent and return success. # What changes are included in this PR? - The registry gains an `unsynced` set. A creation that fails after reaching a directory records the deepest registered directory it found existing and the registered directories above it (also when a later `create_dir` in the same call fails); a creation that reached none records nothing. The directories above the deepest one count whether this call or an untracked creator made them: the creation registered them, so its sync walk covers them, and a failed walk leaves them to the next write too. `fsync_parent_dir` fsyncs from its parent up to the parent of the highest unsynced ancestor, then clears the records on that chain. - `fsync_parent_dir_with` takes the sync function so tests record the sync order; production passes `fsync_dir`. Bound: one entry per registered directory that existed when a creation failed (the deepest one reached, and its registered ancestors, which exist because it does); registered directories below the deepest reached one did not exist and are not recorded. An entry lives until a write under it. While any entry exists, every durable write in the process walks its ancestors under the lock (the empty-registry fast path is off). Not changed: a record is not removed when its directory is deleted without a write; and `fsync_parent_dir`'s `retain` can drop a record if, between its fsyncs and the `retain`, another call deletes the file, cleanup removes a directory in between and a third creation recreates it and fails to sync it. Tests, and how each fails without the fix: - `fsync_write_syncs_directories_a_failed_creation_left`: the sync closure fails during creation; without the set the next write under `a/b` syncs `[a/b]` instead of `[a/b, a, root]`. The second write syncs `[a/b]` only. - `fsync_failed_creation_records_the_directories_it_reached`: a creation that fails on a file as path component records nothing; one that created `n` and then fails records `[n]`; without the set the record is `[]`. - `fsync_failed_creation_records_a_directory_another_creator_made`: `a` appears (untracked creator) after the snapshot found it missing and before the first `create_dir(a/b)`; the sync fails. Records `[a, a/b]`, and the next write under `a/b` syncs `[a/b, a, root]`. Second half: the creation fails after making `a/b` (`a/b/c` appears as a file); records `[a, a/b]`. Recording only the directories this call's `create_dir` ran on gives `[a/b]` and the next write never syncs the root. Locally: `cargo fmt --check`, the CI clippy matrix with `-D warnings`, `cargo test`, `cargo test --all-features`, rustdoc with `-D warnings`, `cargo +1.85.0 check --all-targets --all-features`; `local::tests` ten times in a row. Not run on Windows, and not tested under real power loss. # Are there any user-facing changes? With fsync enabled, the first write under directories a failed creation left also fsyncs them and their parent. No API changes. # AI usage Written with AI assistance; I reviewed the changes and tests. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
