mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 08:38:58 +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 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))
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
// so `mc admin replicate info` and the console show the correct state for healthy peers.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user