mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
perf(ecstore): snapshot per-IO env config on local disk read/write paths (#4736)
This commit is contained in:
@@ -444,14 +444,46 @@ enum LocalReadCopyMethod {
|
||||
DirectReadCopy,
|
||||
}
|
||||
|
||||
/// Check if O_DIRECT reads are enabled.
|
||||
fn is_direct_io_read_enabled() -> bool {
|
||||
rustfs_utils::get_env_bool(ENV_RUSTFS_OBJECT_DIRECT_IO_READ_ENABLE, DEFAULT_RUSTFS_OBJECT_DIRECT_IO_READ_ENABLE)
|
||||
/// Snapshot an env-sourced read-path switch once, then serve it from memory.
|
||||
///
|
||||
/// The value comes from the process environment, which is fixed at launch, so a
|
||||
/// per-read `std::env::var` (a global lock plus a `String` allocation, and for
|
||||
/// bools a `to_ascii_lowercase`) is pure hot-path tax at the IOPS these reads run
|
||||
/// at. In production the closure runs once via `LazyLock`; under `cfg(test)` it
|
||||
/// runs every call so `temp_env` overrides stay observable and tests keep their
|
||||
/// isolation. Invalid-value fallback (backlog#1130) lives in the closure, so it
|
||||
/// is identical on both paths.
|
||||
macro_rules! cached_read_env {
|
||||
($(#[$meta:meta])* fn $name:ident() -> $ty:ty = $read:expr;) => {
|
||||
$(#[$meta])*
|
||||
#[inline]
|
||||
fn $name() -> $ty {
|
||||
fn read_env() -> $ty {
|
||||
$read
|
||||
}
|
||||
#[cfg(test)]
|
||||
{
|
||||
read_env()
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
static VALUE: std::sync::LazyLock<$ty> = std::sync::LazyLock::new(read_env as fn() -> $ty);
|
||||
*VALUE
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Check if O_DIRECT shard/part data writes are enabled.
|
||||
fn is_direct_io_write_enabled() -> bool {
|
||||
rustfs_utils::get_env_bool(ENV_RUSTFS_OBJECT_DIRECT_IO_WRITE_ENABLE, DEFAULT_RUSTFS_OBJECT_DIRECT_IO_WRITE_ENABLE)
|
||||
cached_read_env! {
|
||||
/// Check if O_DIRECT reads are enabled.
|
||||
fn is_direct_io_read_enabled() -> bool =
|
||||
rustfs_utils::get_env_bool(ENV_RUSTFS_OBJECT_DIRECT_IO_READ_ENABLE, DEFAULT_RUSTFS_OBJECT_DIRECT_IO_READ_ENABLE);
|
||||
}
|
||||
|
||||
cached_read_env! {
|
||||
/// Check if O_DIRECT shard/part data writes are enabled.
|
||||
fn is_direct_io_write_enabled() -> bool =
|
||||
rustfs_utils::get_env_bool(ENV_RUSTFS_OBJECT_DIRECT_IO_WRITE_ENABLE, DEFAULT_RUSTFS_OBJECT_DIRECT_IO_WRITE_ENABLE);
|
||||
}
|
||||
|
||||
/// Enable the runtime-probed io_uring read backend (backlog#1104). Default:
|
||||
@@ -803,21 +835,30 @@ pub(crate) fn effective_durability(volume: &str) -> DurabilityMode {
|
||||
bucket_durability::lookup(volume).unwrap_or(global)
|
||||
}
|
||||
|
||||
/// Get the O_DIRECT read threshold size.
|
||||
fn get_direct_io_read_threshold() -> usize {
|
||||
rustfs_utils::get_env_usize(ENV_RUSTFS_OBJECT_DIRECT_IO_READ_THRESHOLD, DEFAULT_RUSTFS_OBJECT_DIRECT_IO_READ_THRESHOLD)
|
||||
cached_read_env! {
|
||||
/// Get the O_DIRECT read threshold size.
|
||||
fn get_direct_io_read_threshold() -> usize =
|
||||
rustfs_utils::get_env_usize(ENV_RUSTFS_OBJECT_DIRECT_IO_READ_THRESHOLD, DEFAULT_RUSTFS_OBJECT_DIRECT_IO_READ_THRESHOLD);
|
||||
}
|
||||
|
||||
cached_read_env! {
|
||||
/// Whether mmap reads should fault the mapping in with `MAP_POPULATE`.
|
||||
fn mmap_populate_enabled() -> bool =
|
||||
rustfs_utils::get_env_bool(ENV_RUSTFS_OBJECT_MMAP_POPULATE_ENABLE, DEFAULT_RUSTFS_OBJECT_MMAP_POPULATE_ENABLE);
|
||||
}
|
||||
|
||||
fn should_populate_mmap_read(length: usize) -> bool {
|
||||
length > 0 && rustfs_utils::get_env_bool(ENV_RUSTFS_OBJECT_MMAP_POPULATE_ENABLE, DEFAULT_RUSTFS_OBJECT_MMAP_POPULATE_ENABLE)
|
||||
length > 0 && mmap_populate_enabled()
|
||||
}
|
||||
|
||||
fn local_read_copy_method() -> LocalReadCopyMethod {
|
||||
let method = rustfs_utils::get_env_str(ENV_RUSTFS_OBJECT_MMAP_READ_METHOD, RUSTFS_OBJECT_MMAP_READ_METHOD_MMAP_COPY);
|
||||
match method.as_str() {
|
||||
RUSTFS_OBJECT_MMAP_READ_METHOD_DIRECT_READ_COPY => LocalReadCopyMethod::DirectReadCopy,
|
||||
_ => LocalReadCopyMethod::MmapCopy,
|
||||
}
|
||||
cached_read_env! {
|
||||
fn local_read_copy_method() -> LocalReadCopyMethod = {
|
||||
let method = rustfs_utils::get_env_str(ENV_RUSTFS_OBJECT_MMAP_READ_METHOD, RUSTFS_OBJECT_MMAP_READ_METHOD_MMAP_COPY);
|
||||
match method.as_str() {
|
||||
RUSTFS_OBJECT_MMAP_READ_METHOD_DIRECT_READ_COPY => LocalReadCopyMethod::DirectReadCopy,
|
||||
_ => LocalReadCopyMethod::MmapCopy,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Runtime state for the true O_DIRECT read path (Linux only).
|
||||
@@ -1665,15 +1706,16 @@ fn record_file_cache_reclaim_error(kind: &'static str) {
|
||||
counter!("rustfs_page_cache_reclaim_requests_total", "kind" => kind.to_string(), "result" => "err".to_string()).increment(1);
|
||||
}
|
||||
|
||||
fn bitrot_size_mismatch_retry_count() -> usize {
|
||||
rustfs_utils::get_env_u64(ENV_BITROT_SIZE_MISMATCH_RETRY_COUNT, DEFAULT_BITROT_SIZE_MISMATCH_RETRY_COUNT) as usize
|
||||
cached_read_env! {
|
||||
fn bitrot_size_mismatch_retry_count() -> usize =
|
||||
rustfs_utils::get_env_u64(ENV_BITROT_SIZE_MISMATCH_RETRY_COUNT, DEFAULT_BITROT_SIZE_MISMATCH_RETRY_COUNT) as usize;
|
||||
}
|
||||
|
||||
fn bitrot_size_mismatch_retry_delay() -> Duration {
|
||||
Duration::from_millis(rustfs_utils::get_env_u64(
|
||||
cached_read_env! {
|
||||
fn bitrot_size_mismatch_retry_delay() -> Duration = Duration::from_millis(rustfs_utils::get_env_u64(
|
||||
ENV_BITROT_SIZE_MISMATCH_RETRY_DELAY_MS,
|
||||
DEFAULT_BITROT_SIZE_MISMATCH_RETRY_DELAY_MS,
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
fn is_bitrot_size_mismatch_error(err: &std::io::Error) -> bool {
|
||||
@@ -1900,23 +1942,38 @@ impl AsyncWrite for FileCacheReclaimWriter {
|
||||
}
|
||||
}
|
||||
|
||||
cached_read_env! {
|
||||
fn file_cache_reclaim_write_enabled() -> bool = rustfs_utils::get_env_bool(
|
||||
rustfs_config::ENV_OBJECT_FILE_CACHE_RECLAIM_WRITE_ENABLE,
|
||||
rustfs_config::DEFAULT_OBJECT_FILE_CACHE_RECLAIM_WRITE_ENABLE,
|
||||
);
|
||||
}
|
||||
|
||||
fn should_reclaim_file_cache_after_write(file_size: i64) -> bool {
|
||||
if file_size <= 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !rustfs_utils::get_env_bool(
|
||||
rustfs_config::ENV_OBJECT_FILE_CACHE_RECLAIM_WRITE_ENABLE,
|
||||
rustfs_config::DEFAULT_OBJECT_FILE_CACHE_RECLAIM_WRITE_ENABLE,
|
||||
) {
|
||||
if !file_cache_reclaim_write_enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let threshold = rustfs_utils::get_env_usize(
|
||||
// Same threshold as the read-side reclaim (backlog#1182): reuse its snapshot.
|
||||
file_size as usize >= file_cache_reclaim_threshold()
|
||||
}
|
||||
|
||||
cached_read_env! {
|
||||
fn file_cache_reclaim_read_enabled() -> bool = rustfs_utils::get_env_bool(
|
||||
rustfs_config::ENV_OBJECT_FILE_CACHE_RECLAIM_READ_ENABLE,
|
||||
rustfs_config::DEFAULT_OBJECT_FILE_CACHE_RECLAIM_READ_ENABLE,
|
||||
);
|
||||
}
|
||||
|
||||
cached_read_env! {
|
||||
fn file_cache_reclaim_threshold() -> usize = rustfs_utils::get_env_usize(
|
||||
rustfs_config::ENV_OBJECT_FILE_CACHE_RECLAIM_THRESHOLD,
|
||||
rustfs_config::DEFAULT_OBJECT_FILE_CACHE_RECLAIM_THRESHOLD,
|
||||
);
|
||||
file_size as usize >= threshold
|
||||
}
|
||||
|
||||
fn should_reclaim_file_cache_after_read(length: usize) -> bool {
|
||||
@@ -1924,18 +1981,11 @@ fn should_reclaim_file_cache_after_read(length: usize) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !rustfs_utils::get_env_bool(
|
||||
rustfs_config::ENV_OBJECT_FILE_CACHE_RECLAIM_READ_ENABLE,
|
||||
rustfs_config::DEFAULT_OBJECT_FILE_CACHE_RECLAIM_READ_ENABLE,
|
||||
) {
|
||||
if !file_cache_reclaim_read_enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let threshold = rustfs_utils::get_env_usize(
|
||||
rustfs_config::ENV_OBJECT_FILE_CACHE_RECLAIM_THRESHOLD,
|
||||
rustfs_config::DEFAULT_OBJECT_FILE_CACHE_RECLAIM_THRESHOLD,
|
||||
);
|
||||
length >= threshold
|
||||
length >= file_cache_reclaim_threshold()
|
||||
}
|
||||
|
||||
/// Write-open semantics for [`LocalIoBackend::open_write`].
|
||||
@@ -12830,6 +12880,12 @@ mod test {
|
||||
temp_env::with_var(ENV_BITROT_SIZE_MISMATCH_RETRY_COUNT, Some("7"), || {
|
||||
assert_eq!(bitrot_size_mismatch_retry_count(), 7);
|
||||
});
|
||||
temp_env::with_var_unset(ENV_BITROT_SIZE_MISMATCH_RETRY_DELAY_MS, || {
|
||||
assert_eq!(
|
||||
bitrot_size_mismatch_retry_delay(),
|
||||
Duration::from_millis(DEFAULT_BITROT_SIZE_MISMATCH_RETRY_DELAY_MS)
|
||||
);
|
||||
});
|
||||
temp_env::with_var(ENV_BITROT_SIZE_MISMATCH_RETRY_DELAY_MS, Some("42"), || {
|
||||
assert_eq!(bitrot_size_mismatch_retry_delay(), Duration::from_millis(42));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user