fix(internode): guard msgpack-only JSON fallback (#5180)

Keep internode JSON compatibility fields unless operators explicitly confirm fleet-wide msgpack-only readiness. This prevents a single legacy rollout flag from emptying JSON fields in mixed-version clusters where older peers may still read the legacy JSON payload.

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-07-24 17:57:51 +08:00
committed by GitHub
parent 8ac618e6c2
commit 4963412265
5 changed files with 144 additions and 29 deletions
+35 -6
View File
@@ -1022,9 +1022,8 @@ fn encode_msgpack<T: Serialize>(value: &T) -> Result<Vec<u8>> {
}
/// JSON compatibility string for a dual-encoded (`_bin` + text) request field. Returns an empty
/// string when msgpack-only mode is enabled (grpc-optimization P2-1) so the redundant JSON copy is
/// not sent; otherwise the legacy JSON encoding. Only use for fields whose peer decodes `_bin`
/// first — the paired `_bin` (msgpack) field must always be sent alongside.
/// string only when msgpack-only mode and its explicit fleet confirmation guard are both enabled;
/// otherwise the legacy JSON encoding is retained for old peers.
fn compat_json<T: Serialize>(value: &T) -> Result<String> {
if rustfs_protos::internode_rpc_msgpack_only() {
return Ok(String::new());
@@ -3019,15 +3018,45 @@ mod tests {
#[test]
fn compat_json_dual_writes_by_default() {
// msgpack-only defaults off, so compat_json returns the JSON encoding (dual-write). The
// empty-string (msgpack-only) path is exercised via the env flag in integration, not here,
// to keep this test independent of process-global env state.
let resp = sample_read_multiple_resp("file", b"data");
let json = compat_json(&resp).expect("compat_json should encode");
assert!(!json.is_empty());
assert_eq!(json, serde_json::to_string(&resp).expect("json should encode"));
}
#[test]
fn compat_json_keeps_json_when_msgpack_only_lacks_fleet_confirmation() {
temp_env::with_vars(
[
(rustfs_config::ENV_INTERNODE_RPC_MSGPACK_ONLY, Some("true")),
(rustfs_config::ENV_INTERNODE_RPC_MSGPACK_ONLY_FLEET_CONFIRMED, None::<&str>),
],
|| {
let resp = sample_read_multiple_resp("file", b"data");
let json = compat_json(&resp).expect("compat_json should encode");
assert!(!json.is_empty(), "old JSON peers must remain compatible without fleet confirmation");
assert_eq!(json, serde_json::to_string(&resp).expect("json should encode"));
},
);
}
#[test]
fn compat_json_omits_json_only_after_fleet_confirmation() {
temp_env::with_vars(
[
(rustfs_config::ENV_INTERNODE_RPC_MSGPACK_ONLY, Some("true")),
(rustfs_config::ENV_INTERNODE_RPC_MSGPACK_ONLY_FLEET_CONFIRMED, Some("true")),
],
|| {
let resp = sample_read_multiple_resp("file", b"data");
let json = compat_json(&resp).expect("compat_json should encode");
assert!(json.is_empty(), "msgpack-only may empty JSON only after explicit fleet confirmation");
},
);
}
#[test]
fn read_multiple_response_decode_reports_corrupt_msgpack_item() {
let endpoint = sample_remote_endpoint();