From 6886dca7d1e4dd4d22344d6ac15023446acfef63 Mon Sep 17 00:00:00 2001 From: houseme Date: Sun, 12 Jul 2026 01:36:58 +0800 Subject: [PATCH] feat(ecstore): make GET codec-streaming a single rollout switch (backlog#1183) (#4752) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backlog#1183, staged rollout step. Simplify enabling the zero-duplex GET codec-streaming fast path to a single switch — RUSTFS_GET_CODEC_STREAMING_ROLLOUT (default "off") — now that body/header parity is proven (parity e2e net + bench A/B on backlog#1183). - Add a clean production rollout token "on" (aliases "full"/"production"); the legacy "internal"/"benchmark" tokens remain accepted. - Flip DEFAULT_RUSTFS_GET_CODEC_STREAMING_ENABLE and the two ..._COMPAT_CONFIRMED defaults to true. They are retained as emergency kill-switches (set any to false to force the fast path off) but no longer gate enablement — the rollout switch does. No production behavior change: with no env set the rollout switch defaults to "off", so GET stays on the legacy duplex path exactly as before. Flipping the hard default to on is deferred to a follow-up after a production soak. Note (intentional semantics change): with the rollout switch opted in ("on"/"internal"/"benchmark"), codec streaming now activates without also setting the two ..._COMPAT_CONFIRMED vars — compatibility is confirmed, so those confirmations are baked in. Co-authored-by: heihutu --- crates/ecstore/src/set_disk/mod.rs | 37 +++++++++++++++++++-- crates/ecstore/src/set_disk/read.rs | 51 ++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/crates/ecstore/src/set_disk/mod.rs b/crates/ecstore/src/set_disk/mod.rs index f67b4dd9a..edbc15044 100644 --- a/crates/ecstore/src/set_disk/mod.rs +++ b/crates/ecstore/src/set_disk/mod.rs @@ -429,7 +429,13 @@ const ENV_RUSTFS_GET_OBJECT_METADATA_CACHE_MAX_ENTRIES: &str = "RUSTFS_GET_OBJEC // --- Codec Streaming Configuration --- const ENV_RUSTFS_GET_CODEC_STREAMING_ENABLE: &str = "RUSTFS_GET_CODEC_STREAMING_ENABLE"; -const DEFAULT_RUSTFS_GET_CODEC_STREAMING_ENABLE: bool = false; // Disabled until rollout gates are ready +// Emergency kill-switch, not the primary enablement knob. The single switch that +// turns codec streaming on/off is `RUSTFS_GET_CODEC_STREAMING_ROLLOUT` (default +// `off`). This flag defaults to `true` and only exists to force-disable the fast +// path regardless of rollout (set to `false`). Body/header compatibility is +// confirmed by the GET codec-streaming parity e2e net + bench A/B (backlog#1183), +// so it no longer gates enablement. See `get_codec_streaming_reader_gate`. +const DEFAULT_RUSTFS_GET_CODEC_STREAMING_ENABLE: bool = true; const ENV_RUSTFS_GET_CODEC_STREAMING_MIN_SIZE: &str = "RUSTFS_GET_CODEC_STREAMING_MIN_SIZE"; const DEFAULT_RUSTFS_GET_CODEC_STREAMING_MIN_SIZE: usize = MI_B; @@ -439,6 +445,10 @@ const DEFAULT_RUSTFS_GET_CODEC_STREAMING_RUSTFS_MIN_SIZE: usize = MI_B; const ENV_RUSTFS_GET_CODEC_STREAMING_ENGINE: &str = "RUSTFS_GET_CODEC_STREAMING_ENGINE"; const DEFAULT_RUSTFS_GET_CODEC_STREAMING_ENGINE: &str = GET_CODEC_STREAMING_ENGINE_LEGACY; +// Primary single switch for the codec-streaming GET fast path. `off` (default) +// keeps GET on the legacy duplex path; `on` (aliases `full`/`production`, plus +// the legacy `internal`/`benchmark`) opts it in. Combine with +// `..._ROLLOUT_PCT` for a partial (percentage-based) rollout. const ENV_RUSTFS_GET_CODEC_STREAMING_ROLLOUT: &str = "RUSTFS_GET_CODEC_STREAMING_ROLLOUT"; const DEFAULT_RUSTFS_GET_CODEC_STREAMING_ROLLOUT: &str = "off"; @@ -918,18 +928,35 @@ fn get_codec_streaming_engine() -> GetCodecStreamingEngine { fn get_codec_streaming_rollout() -> GetCodecStreamingRollout { let rollout = rustfs_utils::get_env_str(ENV_RUSTFS_GET_CODEC_STREAMING_ROLLOUT, DEFAULT_RUSTFS_GET_CODEC_STREAMING_ROLLOUT); match rollout.trim() { + // Clean production token. `internal`/`benchmark` remain accepted aliases + // for backward compatibility; all three opt the fast path in. + value + if value.eq_ignore_ascii_case("on") + || value.eq_ignore_ascii_case("full") + || value.eq_ignore_ascii_case("production") => + { + GetCodecStreamingRollout::On + } value if value.eq_ignore_ascii_case("internal") => GetCodecStreamingRollout::Internal, value if value.eq_ignore_ascii_case("benchmark") => GetCodecStreamingRollout::Benchmark, _ => GetCodecStreamingRollout::Off, } } +/// Emergency kill-switch (defaults to `true`). Set +/// `RUSTFS_GET_CODEC_STREAMING_BODY_COMPAT_CONFIRMED=false` to force the fast path +/// off. Body compatibility is confirmed by the parity e2e net + bench (backlog#1183), +/// so this no longer gates enablement — the `..._ROLLOUT` switch does. fn is_get_codec_streaming_body_compat_confirmed() -> bool { - rustfs_utils::get_env_bool(ENV_RUSTFS_GET_CODEC_STREAMING_BODY_COMPAT_CONFIRMED, false) + rustfs_utils::get_env_bool(ENV_RUSTFS_GET_CODEC_STREAMING_BODY_COMPAT_CONFIRMED, true) } +/// Emergency kill-switch (defaults to `true`). Set +/// `RUSTFS_GET_CODEC_STREAMING_HEADER_COMPAT_CONFIRMED=false` to force the fast path +/// off. Header compatibility is confirmed by the parity e2e net + bench (backlog#1183), +/// so this no longer gates enablement — the `..._ROLLOUT` switch does. fn is_get_codec_streaming_header_compat_confirmed() -> bool { - rustfs_utils::get_env_bool(ENV_RUSTFS_GET_CODEC_STREAMING_HEADER_COMPAT_CONFIRMED, false) + rustfs_utils::get_env_bool(ENV_RUSTFS_GET_CODEC_STREAMING_HEADER_COMPAT_CONFIRMED, true) } fn build_get_codec_streaming_decode_engine(erasure: coding::Erasure) -> std::io::Result { @@ -954,7 +981,11 @@ enum GetCodecStreamingDecision { #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum GetCodecStreamingRollout { + /// Default. Codec streaming disabled; GET uses the legacy duplex path. Off, + /// Production enablement token (`on`/`full`/`production`). + On, + /// Backward-compatible aliases that also opt the fast path in. Internal, Benchmark, } diff --git a/crates/ecstore/src/set_disk/read.rs b/crates/ecstore/src/set_disk/read.rs index 5dcc06933..c8b3ec1d7 100644 --- a/crates/ecstore/src/set_disk/read.rs +++ b/crates/ecstore/src/set_disk/read.rs @@ -4840,7 +4840,11 @@ mod tests { } #[test] - fn codec_streaming_reader_gate_defaults_to_disabled() { + fn codec_streaming_reader_gate_defaults_to_off() { + // With no env set the `..._ROLLOUT` switch defaults to `off`, so GET stays + // on the legacy duplex path. The compat kill-switches now default to + // confirmed (backlog#1183), so the fallback reason is the rollout switch, + // not the retired `Disabled`/`*Unconfirmed` reasons. temp_env::with_vars( [ (ENV_RUSTFS_GET_CODEC_STREAMING_ENABLE, None::<&str>), @@ -4853,6 +4857,51 @@ mod tests { let fi = codec_streaming_test_fileinfo(1024, 1); let object_info = codec_streaming_test_object_info(&fi); + assert_eq!( + codec_streaming_reader_gate_for_test(&None, &object_info, &fi, true).decision, + GetCodecStreamingDecision::Fallback(GetCodecStreamingFallbackReason::RolloutNotOptedIn) + ); + }, + ); + } + + #[test] + fn codec_streaming_reader_gate_enabled_by_rollout_switch_alone() { + // The single switch: `..._ROLLOUT=on` opts the fast path in with the + // ENABLE / *_COMPAT_CONFIRMED kill-switches left unset (they default on). + temp_env::with_vars( + [ + (ENV_RUSTFS_GET_CODEC_STREAMING_ENABLE, None::<&str>), + (ENV_RUSTFS_GET_CODEC_STREAMING_ROLLOUT, Some("on")), + (ENV_RUSTFS_GET_CODEC_STREAMING_BODY_COMPAT_CONFIRMED, None::<&str>), + (ENV_RUSTFS_GET_CODEC_STREAMING_HEADER_COMPAT_CONFIRMED, None::<&str>), + (ENV_RUSTFS_GET_CODEC_STREAMING_MIN_SIZE, Some("1")), + ], + || { + let fi = codec_streaming_test_fileinfo(1024, 1); + let object_info = codec_streaming_test_object_info(&fi); + + assert_eq!( + codec_streaming_reader_gate_for_test(&None, &object_info, &fi, true).decision, + GetCodecStreamingDecision::Use + ); + }, + ); + } + + #[test] + fn codec_streaming_reader_gate_force_disabled_by_enable_kill_switch() { + // Even with the rollout switch on, `ENABLE=false` force-disables the fast path. + temp_env::with_vars( + [ + (ENV_RUSTFS_GET_CODEC_STREAMING_ENABLE, Some("false")), + (ENV_RUSTFS_GET_CODEC_STREAMING_ROLLOUT, Some("on")), + (ENV_RUSTFS_GET_CODEC_STREAMING_MIN_SIZE, Some("1")), + ], + || { + let fi = codec_streaming_test_fileinfo(1024, 1); + let object_info = codec_streaming_test_object_info(&fi); + assert_eq!( codec_streaming_reader_gate_for_test(&None, &object_info, &fi, true).decision, GetCodecStreamingDecision::Fallback(GetCodecStreamingFallbackReason::Disabled)