mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-21 11:56:38 +00:00
fix(site-repl): count replicated buckets as in-sync (base64 decode in status path) (#5022)
fix(site-replication): count replicated buckets as in-sync in replicate status `mc admin replicate status` reported "0/N Buckets in sync" with a cross mark for every bucket even when replication was healthy and objects had propagated. build_sr_info stores each bucket's replication_config in the wire form as base64-encoded XML (raw_config_to_base64), but site_replication_config_mismatch XML-parsed that string directly without base64-decoding. The parse always failed, so every bucket with a replication config was reported as a config mismatch (replication_cfg_mismatch = true), which the status endpoint and mc render as out-of-sync. Decode the wire value (tolerant base64 decode, falling back to raw bytes) before XML-parsing. Adds a regression test that feeds the base64 wire form used in production; the existing tests only exercised raw XML, which masked the bug. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: overtrue <anzhengchao@gmail.com>
This commit is contained in:
@@ -3285,7 +3285,13 @@ fn site_replication_config_mismatch<'a>(values: impl Iterator<Item = Option<&'a
|
|||||||
|
|
||||||
let expected_rules = total_sites.saturating_sub(1);
|
let expected_rules = total_sites.saturating_sub(1);
|
||||||
let replicated = values.iter().all(|raw| {
|
let replicated = values.iter().all(|raw| {
|
||||||
deserialize::<ReplicationConfiguration>(raw.as_bytes())
|
// `raw` is the wire form produced by build_sr_info, i.e. base64-encoded XML
|
||||||
|
// (raw_config_to_base64). Decode it before XML-parsing — parsing the base64 text
|
||||||
|
// directly always fails, which would falsely report every replicated bucket as
|
||||||
|
// out-of-sync ("0/N Buckets in sync"). decode_bucket_meta_wire_value falls back to
|
||||||
|
// the raw bytes when the value is not base64, so plain-XML callers still work.
|
||||||
|
let xml = decode_bucket_meta_wire_value(raw);
|
||||||
|
deserialize::<ReplicationConfiguration>(&xml)
|
||||||
.is_ok_and(|config| config.rules.len() == expected_rules && config.rules.iter().all(site_replication_rule_complete))
|
.is_ok_and(|config| config.rules.len() == expected_rules && config.rules.iter().all(site_replication_rule_complete))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -11003,6 +11009,45 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status miscount regression: build_sr_info stores replication_config as base64-encoded XML
|
||||||
|
// (the wire form). site_replication_config_mismatch must decode it before XML-parsing; before
|
||||||
|
// the fix it parsed the base64 text directly, always failed, and reported every replicated
|
||||||
|
// bucket as out-of-sync ("0/N Buckets in sync"). This test feeds the real base64 wire form.
|
||||||
|
#[test]
|
||||||
|
fn test_site_replication_config_mismatch_accepts_base64_wire_form() {
|
||||||
|
let xml = {
|
||||||
|
let config = ReplicationConfiguration {
|
||||||
|
role: String::new(),
|
||||||
|
rules: vec![build_site_replication_rule(
|
||||||
|
"arn:rustfs:replication::dep-b:bucket",
|
||||||
|
1,
|
||||||
|
"site-repl-dep-b",
|
||||||
|
)],
|
||||||
|
};
|
||||||
|
String::from_utf8(serialize(&config).unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
let b64 = BASE64_STANDARD.encode(xml.as_bytes());
|
||||||
|
|
||||||
|
// Both sites present the complete config in base64 wire form → NOT a mismatch.
|
||||||
|
assert_eq!(
|
||||||
|
site_replication_config_mismatch(vec![Some(&b64), Some(&b64)].into_iter(), 2),
|
||||||
|
(2, false),
|
||||||
|
"base64-encoded complete configs on both sites must not be reported as a mismatch"
|
||||||
|
);
|
||||||
|
// The tolerant decode keeps plain-XML callers working too.
|
||||||
|
assert_eq!(
|
||||||
|
site_replication_config_mismatch(vec![Some(&xml), Some(&xml)].into_iter(), 2),
|
||||||
|
(2, false),
|
||||||
|
"raw-XML wire form still parses via the base64 fallback"
|
||||||
|
);
|
||||||
|
// A base64 config present on only one of two sites is still a mismatch.
|
||||||
|
assert_eq!(
|
||||||
|
site_replication_config_mismatch(vec![Some(&b64)].into_iter(), 2),
|
||||||
|
(1, true),
|
||||||
|
"config present on only one site is a mismatch regardless of encoding"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// BUG1: peers persisted on add/join must carry a real sync_state (Enable), not Unknown,
|
// BUG1: peers persisted on add/join must carry a real sync_state (Enable), not Unknown,
|
||||||
// so `mc admin replicate info` and the console show the correct state for healthy peers.
|
// so `mc admin replicate info` and the console show the correct state for healthy peers.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user