From 968ec4a8be3a3eda9d669f5917bdd4bfb87d9be7 Mon Sep 17 00:00:00 2001 From: houseme Date: Wed, 12 Aug 2026 10:06:39 +0800 Subject: [PATCH] perf(get): enable inline direct-read by default + versioned buckets (#1802) (#5966) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A small object whose data shards are inlined in xl.meta can be reassembled straight from the already-resolved metadata, skipping the Erasure reconstruct pipeline. The fast path existed but was opt-in (`RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY`, default off), so every deployment paid the full shard-read fan-out for eligible small GETs by default. - Flip `DEFAULT_RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY` to `true`. The path is correctness-neutral on a miss: `try_get_object_direct_data_shards_*` returns None when the inline reassembly cannot satisfy the read, and the GET then proceeds through the normal shard-read pipeline. The env stays as a kill switch (`=false` restores the legacy path). - Drop the bucket-level `versioned` / `version_suspended` exclusions. The decision is made on `fi` — the already-resolved target version — so reassembling its inlined data is correct on a versioned bucket too. An explicit versionId GET still falls back (`opts.version_id`), and a delete-marker latest is still rejected. The two now-unused fallback reasons and their metric labels are removed. - Tests updated: a versioned latest-version object is now eligible / `Use` (covers the newly allowed path); the removed reasons' label assertions are dropped. cargo check --lib --tests and the direct_memory unit tests pass on macOS (the change is fully cross-platform). Co-authored-by: heihutu --- crates/ecstore/src/set_disk/mod.rs | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/ecstore/src/set_disk/mod.rs b/crates/ecstore/src/set_disk/mod.rs index ac72a4588..8c9955fca 100644 --- a/crates/ecstore/src/set_disk/mod.rs +++ b/crates/ecstore/src/set_disk/mod.rs @@ -671,7 +671,13 @@ const ENV_RUSTFS_GET_CODEC_STREAMING_DATA_BLOCKS_FIRST_MAX_SIZE: &str = "RUSTFS_ const DEFAULT_RUSTFS_GET_CODEC_STREAMING_DATA_BLOCKS_FIRST_MAX_SIZE: usize = 512 * 1024; const ENV_RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY: &str = "RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY"; -const DEFAULT_RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY: bool = false; +// On by default (rustfs/backlog#1802): a small object whose data shards are +// inlined in xl.meta is reassembled straight from the already-resolved +// metadata, skipping the Erasure reconstruct pipeline. The path has a complete +// fallback — if the inline reassembly returns None, the GET proceeds through +// the normal shard-read pipeline, so a miss is correctness-neutral. Set to +// `false` to force the legacy path (kill switch). +const DEFAULT_RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY: bool = true; const ENV_RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY_THRESHOLD: &str = "RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY_THRESHOLD"; const DEFAULT_RUSTFS_GET_SMALL_OBJECT_DIRECT_MEMORY_THRESHOLD: usize = 128 * 1024; @@ -1614,8 +1620,6 @@ enum GetDirectMemoryFallbackReason { Range, PartNumber, VersionId, - Versioned, - VersionSuspended, InclFreeVersions, SkipFreeVersion, DataMovement, @@ -1641,8 +1645,6 @@ impl GetDirectMemoryFallbackReason { Self::Range => "range", Self::PartNumber => "part_number", Self::VersionId => "version_id", - Self::Versioned => "versioned", - Self::VersionSuspended => "version_suspended", Self::InclFreeVersions => "incl_free_versions", Self::SkipFreeVersion => "skip_free_version", Self::DataMovement => "data_movement", @@ -1766,12 +1768,11 @@ fn get_small_object_direct_memory_decision_with_threshold( if opts.version_id.is_some() { return GetDirectMemoryDecision::Fallback(GetDirectMemoryFallbackReason::VersionId); } - if opts.versioned { - return GetDirectMemoryDecision::Fallback(GetDirectMemoryFallbackReason::Versioned); - } - if opts.version_suspended { - return GetDirectMemoryDecision::Fallback(GetDirectMemoryFallbackReason::VersionSuspended); - } + // Bucket-level versioning no longer blocks the inline path (rustfs/backlog#1802): + // `fi` here is the already-resolved target version, so reassembling its inlined + // data shards is correct whether the bucket is versioned or not. This direct-memory + // decision still falls back for an explicit versionId (the `version_id` check above); + // a delete-marker latest is rejected below. if opts.incl_free_versions { return GetDirectMemoryDecision::Fallback(GetDirectMemoryFallbackReason::InclFreeVersions); } @@ -8681,9 +8682,11 @@ mod tests { 128 * 1024 )); + // Bucket-level versioning no longer blocks the inline path (rustfs/backlog#1802): + // a latest-version read on a versioned bucket is eligible. let mut versioned_opts = opts.clone(); versioned_opts.versioned = true; - assert!(!is_get_small_object_direct_memory_eligible_with_threshold( + assert!(is_get_small_object_direct_memory_eligible_with_threshold( &None, &object_info, &fi, @@ -8744,11 +8747,13 @@ mod tests { GetDirectMemoryDecision::Fallback(GetDirectMemoryFallbackReason::Range) ); + // Bucket-level versioning no longer falls back (rustfs/backlog#1802): the + // latest version on a versioned bucket is served inline like any other. let mut versioned_opts = opts.clone(); versioned_opts.versioned = true; assert_eq!( get_small_object_direct_memory_decision_with_threshold(&None, &object_info, &fi, &versioned_opts, true, 128 * 1024), - GetDirectMemoryDecision::Fallback(GetDirectMemoryFallbackReason::Versioned) + GetDirectMemoryDecision::Use { object_size: 1024 } ); let mut encrypted = object_info.clone(); @@ -8796,8 +8801,6 @@ mod tests { assert_eq!(GetDirectMemoryFallbackReason::Range.as_str(), "range"); assert_eq!(GetDirectMemoryFallbackReason::PartNumber.as_str(), "part_number"); assert_eq!(GetDirectMemoryFallbackReason::VersionId.as_str(), "version_id"); - assert_eq!(GetDirectMemoryFallbackReason::Versioned.as_str(), "versioned"); - assert_eq!(GetDirectMemoryFallbackReason::VersionSuspended.as_str(), "version_suspended"); assert_eq!(GetDirectMemoryFallbackReason::InclFreeVersions.as_str(), "incl_free_versions"); assert_eq!(GetDirectMemoryFallbackReason::SkipFreeVersion.as_str(), "skip_free_version"); assert_eq!(GetDirectMemoryFallbackReason::DataMovement.as_str(), "data_movement");