fix: return 503 on lock contention instead of 500 (#3274)

* fix(ecstore): skip hidden metadata in walk limit

* fix(ecstore): match walk limit to visible versions

* fix(ecstore): avoid decoding all versions for limit

* fix: return 503 on lock contention instead of 500

When concurrent PUTs to the same key contend for the namespace write
lock, lock timeout and conflict errors were wrapped as
StorageError::other(...) → StorageError::Io(...), which fell through
to S3ErrorCode::InternalError (500) in the error mapping.

Only QuorumNotReached was correctly mapped to ServiceUnavailable (503);
all other lock errors (Timeout, AlreadyLocked, etc.) became 500.

Fix: map_namespace_lock_error now returns StorageError::Lock(err) for
non-quorum lock errors, and StorageError::Lock is mapped to
S3ErrorCode::ServiceUnavailable in the HTTP error conversion.

Affected paths:
- crates/ecstore/src/set_disk/lock.rs
- crates/ecstore/src/store/object.rs
- crates/ecstore/src/store/bucket.rs (make_bucket, delete_bucket)
- rustfs/src/app/object_usecase.rs (copy_object)

Regression test: test_concurrent_put_same_key_never_returns_500

* test: fail on unexpected lock contention errors

---------

Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
安正超
2026-06-08 10:20:21 +08:00
committed by GitHub
parent c479f3d0cb
commit 78df276d25
6 changed files with 118 additions and 8 deletions
+1 -4
View File
@@ -67,10 +67,7 @@ impl SetDisks {
achieved,
}
}
other => StorageError::other(format!(
"Failed to acquire {mode} lock: {}",
self.format_lock_error_from_error(bucket, object, mode, &other)
)),
other => StorageError::Lock(other),
}
}
+2 -2
View File
@@ -81,7 +81,7 @@ impl ECStore {
achieved,
}
}
other => StorageError::other(format!("make_bucket: failed to acquire write lock on {bucket}: {other}")),
other => StorageError::Lock(other),
})?,
)
} else {
@@ -185,7 +185,7 @@ impl ECStore {
achieved,
}
}
other => StorageError::other(format!("delete_bucket: failed to acquire write lock on {bucket}: {other}")),
other => StorageError::Lock(other),
})?,
)
} else {
+1 -1
View File
@@ -243,7 +243,7 @@ impl ECStore {
required,
achieved,
},
other => StorageError::other(format!("Failed to acquire {mode} lock on {bucket}/{object}: {other}")),
other => StorageError::Lock(other),
}
}