diff --git a/crates/config/README.md b/crates/config/README.md index e457a60fc..e5fae0cae 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -76,6 +76,11 @@ Current guidance: - `RUSTFS_SCANNER_CYCLE_MAX_OBJECTS` (canonical) - `RUSTFS_SCANNER_CYCLE_MAX_DIRECTORIES` (canonical) +## Mmap read environment aliases + +- `RUSTFS_OBJECT_MMAP_READ_ENABLE` (canonical) +- `RUSTFS_OBJECT_ZERO_COPY_ENABLE` (deprecated alias for compatibility) + ## Health compatibility switches - `RUSTFS_HEALTH_ENDPOINT_ENABLE` diff --git a/crates/config/src/constants/zero_copy.rs b/crates/config/src/constants/zero_copy.rs index 84e300d38..50f217eb3 100644 --- a/crates/config/src/constants/zero_copy.rs +++ b/crates/config/src/constants/zero_copy.rs @@ -15,8 +15,9 @@ //! Mmap-based read I/O configuration constants. //! //! This module defines environment variables and default values for mmap-based -//! read operations. Note: despite the "zero_copy" naming in env vars (kept for -//! backward compatibility), the actual implementation performs mmap-then-copy, +//! read operations. Note: the legacy "zero_copy" env var is kept as a +//! deprecated compatibility alias; the actual implementation performs +//! mmap-then-copy, not true zero-copy. // ============================================================================= // Mmap Read Configuration @@ -32,9 +33,13 @@ /// - Acceptable values: `"true"` / `"false"` (case-insensitive) or a boolean typed config /// - Semantics: When enabled, uses mmap on Unix systems for memory-mapped file reads; /// falls back to regular I/O on non-Unix platforms or when mmap fails -/// - Example: `export RUSTFS_OBJECT_ZERO_COPY_ENABLE=true` -/// - Note: The env var name is kept as ZERO_COPY for backward compatibility. -/// The actual behavior is mmap-then-copy, not true zero-copy. +/// - Example: `export RUSTFS_OBJECT_MMAP_READ_ENABLE=true` +pub const ENV_OBJECT_MMAP_READ_ENABLE: &str = "RUSTFS_OBJECT_MMAP_READ_ENABLE"; + +/// Deprecated compatibility alias for mmap-based read enable. +/// +/// Prefer [`ENV_OBJECT_MMAP_READ_ENABLE`]. When both variables are set, the +/// canonical mmap-read variable takes precedence. pub const ENV_OBJECT_ZERO_COPY_ENABLE: &str = "RUSTFS_OBJECT_ZERO_COPY_ENABLE"; /// Default: mmap-based reads are enabled. @@ -45,7 +50,12 @@ pub const ENV_OBJECT_ZERO_COPY_ENABLE: &str = "RUSTFS_OBJECT_ZERO_COPY_ENABLE"; /// /// On non-Unix platforms or when mmap fails, the system automatically falls back /// to regular I/O without errors. -pub const DEFAULT_OBJECT_ZERO_COPY_ENABLE: bool = true; +pub const DEFAULT_OBJECT_MMAP_READ_ENABLE: bool = true; + +/// Deprecated compatibility alias for mmap-based read default. +/// +/// Prefer [`DEFAULT_OBJECT_MMAP_READ_ENABLE`]. +pub const DEFAULT_OBJECT_ZERO_COPY_ENABLE: bool = DEFAULT_OBJECT_MMAP_READ_ENABLE; // ============================================================================= // Direct I/O Configuration diff --git a/crates/ecstore/src/io_support/bitrot.rs b/crates/ecstore/src/io_support/bitrot.rs index 571862288..dcade2af2 100644 --- a/crates/ecstore/src/io_support/bitrot.rs +++ b/crates/ecstore/src/io_support/bitrot.rs @@ -15,6 +15,7 @@ use crate::disk::{self, DiskAPI as _, DiskStore, FileReader, error::DiskError}; use crate::erasure::coding::{BitrotReader, BitrotWriterWrapper, CustomWriter}; use bytes::Bytes; +use rustfs_config::{DEFAULT_OBJECT_MMAP_READ_ENABLE, ENV_OBJECT_MMAP_READ_ENABLE, ENV_OBJECT_ZERO_COPY_ENABLE}; use rustfs_utils::HashAlgorithm; use std::future::Future; use std::io::{self, Cursor}; @@ -28,6 +29,14 @@ use tracing::debug; type BoxedObjectReader = Box; type OpenObjectReaderFuture = Pin>> + Send>>; +pub(crate) fn object_mmap_read_enabled() -> bool { + rustfs_utils::get_env_bool_with_aliases( + ENV_OBJECT_MMAP_READ_ENABLE, + &[ENV_OBJECT_ZERO_COPY_ENABLE], + DEFAULT_OBJECT_MMAP_READ_ENABLE, + ) +} + #[derive(Clone)] struct BitrotReaderSource { inline_data: Option, @@ -295,6 +304,32 @@ pub async fn create_bitrot_writer( mod tests { use super::*; + #[test] + fn object_mmap_read_enabled_accepts_legacy_zero_copy_alias() { + temp_env::with_vars( + [ + (ENV_OBJECT_MMAP_READ_ENABLE, None::<&str>), + (ENV_OBJECT_ZERO_COPY_ENABLE, Some("false")), + ], + || { + assert!(!object_mmap_read_enabled()); + }, + ); + } + + #[test] + fn object_mmap_read_enabled_prefers_canonical_env() { + temp_env::with_vars( + [ + (ENV_OBJECT_MMAP_READ_ENABLE, Some("true")), + (ENV_OBJECT_ZERO_COPY_ENABLE, Some("false")), + ], + || { + assert!(object_mmap_read_enabled()); + }, + ); + } + #[tokio::test] async fn test_create_bitrot_reader_with_inline_data() { let test_data = b"hello world test data"; diff --git a/crates/ecstore/src/set_disk/heal.rs b/crates/ecstore/src/set_disk/heal.rs index 2103e91a8..3b71027d6 100644 --- a/crates/ecstore/src/set_disk/heal.rs +++ b/crates/ecstore/src/set_disk/heal.rs @@ -13,8 +13,8 @@ // limitations under the License. use super::*; +use crate::io_support::bitrot::object_mmap_read_enabled; use crate::storage_api_contracts::namespace::NamespaceLocking as _; -use rustfs_config::{DEFAULT_OBJECT_ZERO_COPY_ENABLE, ENV_OBJECT_ZERO_COPY_ENABLE}; const LOG_COMPONENT_ECSTORE: &str = "ecstore"; const LOG_SUBSYSTEM_HEAL: &str = "heal"; @@ -360,10 +360,7 @@ impl SetDisks { for (part_index, part) in latest_meta.parts.iter().enumerate() { let till_offset = erasure.shard_file_offset(0, part.size, part.size); - // Read zero-copy configuration from environment variable - // Default: enabled (true) for performance - let use_mmap_read = - rustfs_utils::get_env_bool(ENV_OBJECT_ZERO_COPY_ENABLE, DEFAULT_OBJECT_ZERO_COPY_ENABLE); + let use_mmap_read = object_mmap_read_enabled(); let mut readers = Vec::with_capacity(latest_disks.len()); let mut writers = Vec::with_capacity(out_dated_disks.len()); diff --git a/crates/ecstore/src/set_disk/read.rs b/crates/ecstore/src/set_disk/read.rs index 76227dc50..29b00bf0e 100644 --- a/crates/ecstore/src/set_disk/read.rs +++ b/crates/ecstore/src/set_disk/read.rs @@ -26,11 +26,10 @@ use crate::diagnostics::get::{ record_get_object_pipeline_failure_for_path, record_get_stage_duration_if_enabled, }; use crate::erasure::coding::BitrotReader; -use crate::io_support::bitrot::create_deferred_bitrot_reader; +use crate::io_support::bitrot::{create_deferred_bitrot_reader, object_mmap_read_enabled}; use crate::set_disk::shard_source::ShardReadCost; use futures::stream::{FuturesUnordered, StreamExt}; use metrics::counter; -use rustfs_config::{DEFAULT_OBJECT_ZERO_COPY_ENABLE, ENV_OBJECT_ZERO_COPY_ENABLE}; use std::{ collections::{HashMap, VecDeque}, future::Future, @@ -2004,9 +2003,7 @@ impl SetDisks { }; let read_length = till_offset.saturating_sub(read_offset); - // Read zero-copy configuration from environment variable - // Default: enabled (true) for performance - let use_mmap_read = rustfs_utils::get_env_bool(ENV_OBJECT_ZERO_COPY_ENABLE, DEFAULT_OBJECT_ZERO_COPY_ENABLE); + let use_mmap_read = object_mmap_read_enabled(); let reader_setup_stage_start = Instant::now(); let read_costs = disks @@ -2387,7 +2384,7 @@ impl SetDisks { } else { checksum_info.algorithm }; - let use_mmap_read = rustfs_utils::get_env_bool(ENV_OBJECT_ZERO_COPY_ENABLE, DEFAULT_OBJECT_ZERO_COPY_ENABLE); + let use_mmap_read = object_mmap_read_enabled(); let till_offset = erasure.shard_file_offset(part_offset, part_length, part_size); let read_offset = (part_offset / erasure.block_size) * erasure.shard_size(); let read_length = till_offset.saturating_sub(read_offset);