fix(rebalance): converge multipart data movement retries (#6057)

* fix(rebalance): converge multipart data movement retries

* fix(rebalance): harden multipart retry replacement

* fix(rebalance): isolate internal multipart uploads

* test(ecstore): adapt metadata mutation fixtures

* fix(rebalance): preserve transition metadata semantics

* refactor(ecstore): reuse internal metadata matcher

* Revert "refactor(ecstore): reuse internal metadata matcher"

This reverts commit c87ca0328f.

* refactor(rebalance): reuse data movement log constants

* fix(rebalance): isolate migration-owned state

* fix(rebalance): preserve pre-gate retry compatibility
This commit is contained in:
cxymds
2026-08-13 14:12:26 +08:00
committed by GitHub
parent 11eecdc888
commit e11fcfbd08
36 changed files with 6725 additions and 845 deletions
+22 -2
View File
@@ -30,6 +30,10 @@ pub const MINIO_INTERNAL_PREFIX: &str = "x-minio-internal-";
// Key suffixes (lowercase, no prefix)
pub const SUFFIX_INLINE_DATA: &str = "inline-data";
pub const SUFFIX_DATA_MOVED: &str = "data-moved";
/// Tags snapshot bound to a data-movement-owned target.
pub const SUFFIX_DATA_MOVED_TAGS: &str = "data-moved-tags";
/// Internal ownership marker for a data-movement multipart upload.
pub const SUFFIX_DATA_MOVEMENT_UPLOAD: &str = "data-movement-upload";
/// Transient flag for data movement
pub const SUFFIX_DATA_MOV: &str = "data-mov";
/// Transient flag for healing
@@ -44,6 +48,8 @@ pub const SUFFIX_ACTUAL_OBJECT_SIZE: &str = "actual-object-size";
/// Used by replication; key stored with capital A
pub const SUFFIX_ACTUAL_OBJECT_SIZE_CAP: &str = "Actual-Object-Size";
pub const SUFFIX_CRC: &str = "crc";
/// JSON-encoded per-part S3 checksum maps retained across raw data movement.
pub const SUFFIX_PART_CHECKSUMS: &str = "part-checksums";
pub const SUFFIX_TRANSITION_STATUS: &str = "transition-status";
pub const SUFFIX_TRANSITIONED_OBJECTNAME: &str = "transitioned-object";
pub const SUFFIX_TRANSITIONED_VERSION_ID: &str = "transitioned-versionID";
@@ -132,8 +138,10 @@ pub fn internal_key_starts_with(key: &str, suffix_prefix: &str) -> bool {
/// For keys like x-rustfs-internal-replication-reset-{arn}, strips the internal prefix and suffix_prefix,
/// returning the remainder (e.g. "arn1"). Returns None if key does not match.
pub fn internal_key_strip_suffix_prefix(key: &str, suffix_prefix: &str) -> Option<String> {
let rest = strip_internal_prefix(key)?;
rest.strip_prefix(suffix_prefix).map(|s| s.to_string())
let rest = strip_internal_prefix_preserving_case(key)?;
rest.get(..suffix_prefix.len())
.is_some_and(|prefix| prefix.eq_ignore_ascii_case(suffix_prefix))
.then(|| rest[suffix_prefix.len()..].to_string())
}
fn both_keys(suffix: &str) -> (String, String) {
@@ -529,6 +537,18 @@ mod tests {
assert!(!has_internal_suffix(key, SUFFIX_COMPRESSION));
}
#[test]
fn internal_suffix_prefix_preserves_dynamic_identifier_case() {
assert_eq!(
internal_key_strip_suffix_prefix(
"X-Minio-Internal-Replication-Reset-arn:minio:replication::TenantA:bucket",
SUFFIX_REPLICATION_RESET_ARN_PREFIX,
)
.as_deref(),
Some("arn:minio:replication::TenantA:bucket")
);
}
#[test]
fn test_get_str_case_insensitive_fallback() {
// Non-canonical mixed-case key must still be found via the case-insensitive scan.