fix(fuse,core): retry a create at once when a write replaced its blob (B185)

This commit is contained in:
nvrl committed 2026-10-05 02:34:05 +02:00
1 parent 93f02672e6
commit d26dc8ada4
6 files changed
+80 -11

No files matched your search

+9 -3
View File
@@ -1477,22 +1477,28 @@ impl Db {
/// Make a create due again if it was renamed or moved
/// ([`Db::rewrite_op_target`]) while the attempt that just failed was on
/// the wire, `parent_uid` and `name` being what that attempt sent. What it
/// failed on may have been the target it no longer has. Returns whether it
/// failed on may have been the target it no longer has. Likewise if a
/// write replaced its blob (`blob_path` being the one the attempt read):
/// the old one is discarded as the new one attaches, and an attempt that
/// had not opened it yet failed on a file that is gone. Returns whether it
/// was.
pub fn retry_if_retargeted(
&self,
id: i64,
parent_uid: Option<&str>,
name: Option<&str>,
blob_path: Option<&str>,
) -> Result<bool> {
let conn = self.conn.lock();
let n = conn.execute(
&format!(
"UPDATE pending_op SET next_attempt_at = 0
WHERE id = ?1 AND kind IN (?4, ?5) AND next_attempt_at < ?6
AND ({PARENT_NOW} IS NOT ?2 OR name IS NOT ?3)"
AND ({PARENT_NOW} IS NOT ?2 OR name IS NOT ?3 OR blob_path IS NOT ?7)"
),
params![id, parent_uid, name, OP_CREATE, OP_MKDIR, PARK_UNTIL],
params![
id, parent_uid, name, OP_CREATE, OP_MKDIR, PARK_UNTIL, blob_path
],
)?;
Ok(n > 0)
}
+45 -5
View File
@@ -2969,7 +2969,8 @@ fn renaming_a_queued_create_rewrites_its_target() {
/// A create that failed on a name held by a queued change waits out a
/// backoff. Renamed away from that name, it is due again at once; parked, it
/// stays parked.
/// stays parked. One whose blob a write replaced as it failed is due again at
/// once too.
#[test]
fn renaming_a_backing_off_create_makes_it_due() {
let db = Db::open_in_memory().unwrap();
@@ -3011,8 +3012,13 @@ fn renaming_a_backing_off_create_makes_it_due() {
db.record_op_failure(sent.id, "d is still held", 10_000)
.unwrap();
assert!(
db.retry_if_retargeted(sent.id, sent.parent_uid.as_deref(), sent.name.as_deref())
.unwrap()
db.retry_if_retargeted(
sent.id,
sent.parent_uid.as_deref(),
sent.name.as_deref(),
sent.blob_path.as_deref()
)
.unwrap()
);
let ops = db.pending_ops().unwrap();
assert_eq!(
@@ -3023,8 +3029,42 @@ fn renaming_a_backing_off_create_makes_it_due() {
0
);
assert!(
!db.retry_if_retargeted(sent.id, sent.parent_uid.as_deref(), Some("f"))
.unwrap()
!db.retry_if_retargeted(
sent.id,
sent.parent_uid.as_deref(),
Some("f"),
sent.blob_path.as_deref()
)
.unwrap()
);
// A write replaced the blob before the attempt opened it: the old one was
// discarded, the attempt failed on a missing file, and the create is due
// again at once with the new one (`docs/BUGS.md` B185).
db.record_op_failure(sent.id, "No such file or directory", 10_000)
.unwrap();
db.attach_blob_to_create("local~failed", "/staging/newer", "{}")
.unwrap()
.expect("the create is still queued");
db.record_op_failure(sent.id, "No such file or directory", 10_000)
.unwrap();
assert!(
db.retry_if_retargeted(
sent.id,
sent.parent_uid.as_deref(),
Some("f"),
sent.blob_path.as_deref()
)
.unwrap()
);
assert!(
!db.retry_if_retargeted(
sent.id,
sent.parent_uid.as_deref(),
Some("f"),
Some("/staging/newer")
)
.unwrap()
);
}
+2 -1
View File
@@ -441,9 +441,10 @@ impl Core {
op.id,
op.parent_uid.as_deref(),
op.name.as_deref(),
op.blob_path.as_deref(),
) {
Ok(true) => {
debug!(uid = %op.uid, "create was renamed while it failed; retrying")
debug!(uid = %op.uid, "create was renamed or rewritten while it failed; retrying")
}
Ok(false) => {}
Err(e) => {