From 7d3f5545e73f5125e7cb069a0ab3983129ab0c2a Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Mon, 24 Aug 2026 16:10:24 +0800 Subject: [PATCH] fix(ci): repair post-merge build gates --- crates/ecstore/src/core/pools.rs | 14 ++-- crates/ecstore/src/disk/local.rs | 5 +- crates/ecstore/src/services/rebalance/mod.rs | 4 +- crates/scanner/src/scanner_folder.rs | 11 +-- crates/scanner/src/storage_api.rs | 26 ++++--- rustfs/Cargo.toml | 3 +- rustfs/src/allocator_reclaim.rs | 20 ++++-- rustfs/src/app/object_usecase.rs | 4 +- rustfs/src/lib.rs | 5 ++ rustfs/src/main.rs | 71 ++++++++++++++++++-- rustfs/src/memory_observability.rs | 17 ++++- 11 files changed, 139 insertions(+), 41 deletions(-) diff --git a/crates/ecstore/src/core/pools.rs b/crates/ecstore/src/core/pools.rs index 26bb1066e..b06d010aa 100644 --- a/crates/ecstore/src/core/pools.rs +++ b/crates/ecstore/src/core/pools.rs @@ -56,6 +56,7 @@ use crate::storage_api_contracts::{ bucket::{BucketOperations, BucketOptions, MakeBucketOptions}, heal::HealOperations as _, list::ListOperations as _, + namespace::NamespaceLocking as _, object::{EcstoreObjectIO, HTTPPreconditions, ObjectIO as _, ObjectOperations as _}, }; use crate::{core::sets::Sets, store::ECStore}; @@ -9035,10 +9036,8 @@ impl ECStore { ) -> Result> { self.ensure_decommission_generation_current(idx, generation).await?; let operation_gate = self.ctx.data_movement_operation_gate(); - self.run_guarded_decommission_side_effect(rx, &operation_gate, || { - self.check_after_decommission_unfenced(idx, generation) - }) - .await + self.run_guarded_decommission_side_effect(rx, &operation_gate, || self.check_after_decommission_unfenced(idx, generation)) + .await } async fn check_after_decommission_unfenced( @@ -11115,10 +11114,9 @@ mod pools_tests { load_decommission_entry_versions, local_decommission_queue_prefix, mark_decommission_bucket_done, merge_decommission_durable_ilm_receipts, merge_pool_meta_updates_for_save, merge_pool_status_refresh, missing_decommission_worker_prefix, observe_decommission_terminal_reload_result, pool_meta_has_active_decommission, - publish_pool_meta_updates, reconcile_decommission_meta_buckets, - reconcile_decommission_unresolved_entries_for_completion, record_decommission_unresolved_entry, - require_decommission_store, - reserve_decommission_start_cancelers, resolve_decommission_bucket_state, resolve_decommission_check_after_list_result, + publish_pool_meta_updates, reconcile_decommission_meta_buckets, reconcile_decommission_unresolved_entries_for_completion, + record_decommission_unresolved_entry, require_decommission_store, reserve_decommission_start_cancelers, + resolve_decommission_bucket_state, resolve_decommission_check_after_list_result, resolve_decommission_entry_cleanup_delete_result, resolve_decommission_entry_exact_versions, resolve_decommission_entry_reload_result, resolve_decommission_listing_worker_result, resolve_decommission_optional_bucket_config_result, resolve_decommission_pool_meta_reload_result, diff --git a/crates/ecstore/src/disk/local.rs b/crates/ecstore/src/disk/local.rs index 317b9ec01..7e0683b5c 100644 --- a/crates/ecstore/src/disk/local.rs +++ b/crates/ecstore/src/disk/local.rs @@ -35,7 +35,10 @@ use crate::disk::{ error::{DiskError, Error, FileAccessDeniedWithContext, Result}, error_conv::{to_access_error, to_file_error, to_unformatted_disk_error, to_volume_error}, format::FormatV3, - fs::{O_APPEND, O_CREATE, O_RDONLY, O_TRUNC, O_WRONLY, access, cached_access, invalidate_bucket_cache, lstat, lstat_std, remove, remove_all_std, remove_std, rename}, + fs::{ + O_APPEND, O_CREATE, O_RDONLY, O_TRUNC, O_WRONLY, access, cached_access, invalidate_bucket_cache, lstat, lstat_std, + remove, remove_all_std, remove_std, rename, + }, is_quota_mutation_fence_path, os, os::{check_path_length, is_dir_not_empty_error, is_empty_dir, is_root_disk, rename_all, rename_all_ignore_missing_source}, quota_mutation_fence_path, diff --git a/crates/ecstore/src/services/rebalance/mod.rs b/crates/ecstore/src/services/rebalance/mod.rs index 16ba2d522..7c55c4ea7 100644 --- a/crates/ecstore/src/services/rebalance/mod.rs +++ b/crates/ecstore/src/services/rebalance/mod.rs @@ -76,7 +76,7 @@ pub async fn test_store_with_persisted_rebalance_meta( rebalance_meta: tokio::sync::RwLock::new(Some(meta)), decommission_cancelers: tokio::sync::RwLock::new(vec![None]), start_gate: tokio::sync::Mutex::new(()), - pool_meta_save_gate: tokio::sync::Mutex::new(()), + pool_meta_save_gate: tokio::sync::Mutex::default(), ctx, bucket_fence_registry: std::sync::Arc::default(), }); @@ -168,7 +168,7 @@ async fn test_two_pool_stores_with_contexts( rebalance_meta: tokio::sync::RwLock::new(rebalance_meta.clone()), decommission_cancelers: tokio::sync::RwLock::new(vec![None, None]), start_gate: tokio::sync::Mutex::new(()), - pool_meta_save_gate: tokio::sync::Mutex::new(()), + pool_meta_save_gate: tokio::sync::Mutex::default(), ctx: store_ctx, bucket_fence_registry: std::sync::Arc::default(), }) diff --git a/crates/scanner/src/scanner_folder.rs b/crates/scanner/src/scanner_folder.rs index 3956c4484..c709fc436 100644 --- a/crates/scanner/src/scanner_folder.rs +++ b/crates/scanner/src/scanner_folder.rs @@ -33,7 +33,13 @@ use crate::scanner_io::{ SCANNER_SKIP_FILE_ERROR, ScannerIODisk as _, is_scanner_metadata_corrupt_error, is_scanner_metadata_transient_error, }; use crate::sleeper::DynamicSleeper; -use crate::storage_api::owner::{EcstoreEventArgs, ecstore_send_event}; +use crate::storage_api::owner::{ + EcstoreBucketLifecycleConfiguration as BucketLifecycleConfiguration, EcstoreEventArgs, + EcstoreLifecycleRuleFilter as LifecycleRuleFilter, EcstoreObjectLockConfiguration as ObjectLockConfiguration, + EcstoreVersioningConfiguration as VersioningConfiguration, ecstore_send_event, +}; +#[cfg(test)] +use crate::storage_api::owner::{EcstoreExpirationStatus as ExpirationStatus, EcstoreLifecycleRule as LifecycleRule}; use metrics::{counter, describe_counter}; use rustfs_common::heal_channel::{ HEAL_DELETE_DANGLING, HealAdmissionDropReason, HealAdmissionResult, HealChannelPriority, HealChannelRequest, @@ -49,9 +55,6 @@ use rustfs_filemeta::{ MetaCacheHealCandidateKind, }; use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf}; -use s3s::dto::{BucketLifecycleConfiguration, LifecycleRuleFilter, ObjectLockConfiguration, VersioningConfiguration}; -#[cfg(test)] -use s3s::dto::{ExpirationStatus, LifecycleRule}; use time::OffsetDateTime; use tokio::select; use tokio::sync::mpsc; diff --git a/crates/scanner/src/storage_api.rs b/crates/scanner/src/storage_api.rs index 7de8c39da..4278fdad8 100644 --- a/crates/scanner/src/storage_api.rs +++ b/crates/scanner/src/storage_api.rs @@ -17,6 +17,13 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; +pub(crate) use s3s::dto::{ + BucketLifecycleConfiguration as EcstoreBucketLifecycleConfiguration, LifecycleRuleFilter as EcstoreLifecycleRuleFilter, + ObjectLockConfiguration as EcstoreObjectLockConfiguration, VersioningConfiguration as EcstoreVersioningConfiguration, +}; +#[cfg(test)] +pub(crate) use s3s::dto::{ExpirationStatus as EcstoreExpirationStatus, LifecycleRule as EcstoreLifecycleRule}; + pub(crate) use rustfs_ecstore::api::bucket::bucket_target_sys::BucketTargetSys as EcstoreBucketTargetSys; pub(crate) use rustfs_ecstore::api::bucket::lifecycle::bucket_lifecycle_audit::LcEventSrc as EcstoreLcEventSrc; pub(crate) use rustfs_ecstore::api::bucket::lifecycle::bucket_lifecycle_ops::{ @@ -111,12 +118,13 @@ pub(crate) mod owner { pub(crate) use super::{ ECSTORE_BUCKET_META_PREFIX, ECSTORE_RUSTFS_META_BUCKET, ECSTORE_STORAGE_FORMAT_FILE, ECSTORE_STORAGECLASS_RRS, - ECSTORE_STORAGECLASS_STANDARD, ECSTORE_TRANSITION_COMPLETE, EcstoreBucketTargetSys, EcstoreBucketVersioningSys, - EcstoreDisk, EcstoreDiskAPI, EcstoreDiskBytes, EcstoreDiskError, EcstoreDiskInfo, EcstoreDiskInfoOptions, - EcstoreDiskLocation, EcstoreDiskResult, EcstoreErrorType, EcstoreEvaluator, EcstoreEvent, EcstoreEventArgs, - EcstoreLcEventSrc, EcstoreLifecycle, EcstoreListPathRawOptions, EcstoreNsScannerOpenRequest, EcstoreObjectOpts, - EcstoreReplicationConfigurationExt, EcstoreReplicationScannerBridge, EcstoreResultType, EcstoreScanGuard, - EcstoreSetDisks, EcstoreStorageError, EcstoreStore, EcstoreVersioningApi, SCANNER_PUBLICATION_LEASE_FENCE_METADATA_KEY, + ECSTORE_STORAGECLASS_STANDARD, ECSTORE_TRANSITION_COMPLETE, EcstoreBucketLifecycleConfiguration, EcstoreBucketTargetSys, + EcstoreBucketVersioningSys, EcstoreDisk, EcstoreDiskAPI, EcstoreDiskBytes, EcstoreDiskError, EcstoreDiskInfo, + EcstoreDiskInfoOptions, EcstoreDiskLocation, EcstoreDiskResult, EcstoreErrorType, EcstoreEvaluator, EcstoreEvent, + EcstoreEventArgs, EcstoreLcEventSrc, EcstoreLifecycle, EcstoreLifecycleRuleFilter, EcstoreListPathRawOptions, + EcstoreNsScannerOpenRequest, EcstoreObjectLockConfiguration, EcstoreObjectOpts, EcstoreReplicationConfigurationExt, + EcstoreReplicationScannerBridge, EcstoreResultType, EcstoreScanGuard, EcstoreSetDisks, EcstoreStorageError, EcstoreStore, + EcstoreVersioningApi, EcstoreVersioningConfiguration, SCANNER_PUBLICATION_LEASE_FENCE_METADATA_KEY, SCANNER_PUBLICATION_LEASE_TTL_MS, ScannerReplicationHealObject, ScannerReplicationHealResult, ScannerReplicationQueueAdmission, ecstore_apply_expiry_rule, ecstore_apply_transition_rule, ecstore_expiry_state_handle, ecstore_get_global_tier_config_mgr, ecstore_get_lifecycle_config, ecstore_get_object_lock_config, @@ -131,9 +139,9 @@ pub(crate) mod owner { #[cfg(test)] pub(crate) use super::{ EcstoreDiskOption, EcstoreDiskStore, EcstoreEndpoint, EcstoreEndpointServerPools, EcstoreEndpoints, - EcstoreInstanceContext, EcstorePoolDecommissionInfo, EcstorePoolEndpoints, EcstoreRebalStatus, EcstoreRebalanceInfo, - EcstoreRebalanceMeta, EcstoreRebalanceStats, ecstore_config_init, ecstore_init_bucket_metadata_sys, - ecstore_init_local_disks_with_instance_ctx, ecstore_new_disk, + EcstoreExpirationStatus, EcstoreInstanceContext, EcstoreLifecycleRule, EcstorePoolDecommissionInfo, EcstorePoolEndpoints, + EcstoreRebalStatus, EcstoreRebalanceInfo, EcstoreRebalanceMeta, EcstoreRebalanceStats, ecstore_config_init, + ecstore_init_bucket_metadata_sys, ecstore_init_local_disks_with_instance_ctx, ecstore_new_disk, }; } diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index 89ccc8a85..5220a16ff 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -57,7 +57,7 @@ pyroscope = ["rustfs-obs/pyroscope"] # Tokio runtime telemetry. Requires `--cfg tokio_unstable`; use `make build-profiling`. dial9 = ["rustfs-obs/dial9"] # Allocator features -mimalloc = ["dep:rustfs-mimalloc", "dep:rustfs-mimalloc-sys"] +mimalloc = ["dep:rustfs-mimalloc"] jemalloc = ["dep:tikv-jemallocator"] hotpath = [ "hotpath/hotpath", @@ -345,7 +345,6 @@ rustfs-mimalloc = { workspace = true, optional = true } libsystemd.workspace = true [target.'cfg(not(target_os = "windows"))'.dependencies] -rustfs-mimalloc-sys = { workspace = true, optional = true } tikv-jemallocator = { version = "0.6", optional = true } [dev-dependencies] diff --git a/rustfs/src/allocator_reclaim.rs b/rustfs/src/allocator_reclaim.rs index eba1a8948..980fc2abe 100644 --- a/rustfs/src/allocator_reclaim.rs +++ b/rustfs/src/allocator_reclaim.rs @@ -169,15 +169,25 @@ impl AllocatorReclaimController { /// Return the allocator backend name used by reclaim and memory metrics. pub fn allocator_backend() -> &'static str { - #[cfg(not(target_os = "windows"))] + #[cfg(all(feature = "mimalloc", not(target_os = "windows")))] { "mimalloc" } - #[cfg(target_os = "windows")] + #[cfg(all(feature = "mimalloc", target_os = "windows"))] { "mimalloc-windows" } + + #[cfg(all(not(feature = "mimalloc"), feature = "jemalloc"))] + { + "jemalloc" + } + + #[cfg(not(any(feature = "mimalloc", feature = "jemalloc")))] + { + "system" + } } fn active_requests() -> u64 { @@ -368,15 +378,15 @@ pub fn allocator_reclaim_controller_snapshot(ctx: &CancellationToken) -> Allocat ) } -#[cfg(not(target_os = "windows"))] +#[cfg(all(feature = "mimalloc", not(target_os = "windows")))] fn collect_allocator_memory(force: bool) -> Result<(), String> { rustfs_mimalloc::MiMalloc::collect(force); Ok(()) } -#[cfg(target_os = "windows")] +#[cfg(not(all(feature = "mimalloc", not(target_os = "windows"))))] fn collect_allocator_memory(_force: bool) -> Result<(), String> { - Err("allocator reclaim is not supported on Windows".to_string()) + Err("allocator reclaim requires mimalloc on a non-Windows target".to_string()) } /// Execute one allocator collection and publish the outcome metrics. diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index d57f81ab0..380b86ef7 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -65,8 +65,6 @@ use super::storage_api::object_usecase::contract::http::HTTPPreconditions; use super::storage_api::object_usecase::contract::namespace::NamespaceLocking; use super::storage_api::object_usecase::contract::object::{ObjectIO as _, ObjectOperations as _}; use super::storage_api::object_usecase::contract::range::HTTPRangeSpec; -#[cfg(test)] -use super::storage_api::object_usecase::data_usage::apply_bucket_usage_memory_overlay; use super::storage_api::object_usecase::data_usage::{ quota_object_size, record_bucket_delete_marker_memory, record_bucket_object_delete_memory, record_bucket_object_version_write_memory, record_bucket_object_write_memory, @@ -18082,7 +18080,7 @@ mod tests { async fn observed_bucket_usage(bucket: &str) -> Option { let mut usage = rustfs_data_usage::DataUsageInfo::default(); - crate::app::storage_api::object_usecase::data_usage::apply_bucket_usage_memory_overlay(&mut usage).await; + apply_bucket_usage_memory_overlay(&mut usage).await; usage.buckets_usage.get(bucket).map(|value| value.size) } diff --git a/rustfs/src/lib.rs b/rustfs/src/lib.rs index a1836f4f3..e4316aa01 100644 --- a/rustfs/src/lib.rs +++ b/rustfs/src/lib.rs @@ -52,6 +52,11 @@ //! tests where you start one server in a background task, run all your //! tests, and then shut it down. +#[cfg(all(feature = "mimalloc", feature = "jemalloc"))] +compile_error!("allocator features 'mimalloc' and 'jemalloc' are mutually exclusive"); +#[cfg(all(feature = "jemalloc", target_os = "windows"))] +compile_error!("allocator feature 'jemalloc' is not supported on Windows"); + /// Scope-based hotpath measurement for `#[async_trait]` methods, where /// `#[hotpath::measure]` would only time the boxed-future construction. /// The guard records wall time from this statement until the enclosing diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index 7f4bea1d2..a0b60321a 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -12,14 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(all(feature = "hotpath", feature = "hotpath-alloc"))] +#[cfg(all( + feature = "hotpath", + feature = "hotpath-alloc", + feature = "mimalloc", + not(feature = "jemalloc") +))] use std::alloc::{GlobalAlloc, Layout}; -#[cfg(all(feature = "hotpath", feature = "hotpath-alloc"))] +#[cfg(all( + feature = "hotpath", + feature = "hotpath-alloc", + feature = "mimalloc", + not(feature = "jemalloc") +))] #[derive(Default)] struct MiMallocAllocator; -#[cfg(all(feature = "hotpath", feature = "hotpath-alloc"))] +#[cfg(all( + feature = "hotpath", + feature = "hotpath-alloc", + feature = "mimalloc", + not(feature = "jemalloc") +))] // SAFETY: allocation operations are forwarded unchanged to MiMalloc, so // MiMalloc's GlobalAlloc guarantees apply to every returned pointer and layout. #[allow(unsafe_code)] @@ -45,21 +60,65 @@ unsafe impl GlobalAlloc for MiMallocAllocator { } } -#[cfg(all(feature = "hotpath", feature = "hotpath-alloc"))] +#[cfg(all( + feature = "hotpath", + feature = "hotpath-alloc", + feature = "mimalloc", + not(feature = "jemalloc") +))] #[global_allocator] static GLOBAL: hotpath::CountingAllocator = hotpath::CountingAllocator::with(MiMallocAllocator); -#[cfg(not(all(feature = "hotpath", feature = "hotpath-alloc")))] +#[cfg(all( + feature = "hotpath", + feature = "hotpath-alloc", + feature = "jemalloc", + not(feature = "mimalloc"), + not(target_os = "windows") +))] +#[global_allocator] +static GLOBAL: hotpath::CountingAllocator = + hotpath::CountingAllocator::with(tikv_jemallocator::Jemalloc); + +#[cfg(all( + feature = "hotpath", + feature = "hotpath-alloc", + not(any(feature = "mimalloc", feature = "jemalloc")) +))] +#[global_allocator] +static GLOBAL: hotpath::CountingAllocator = hotpath::CountingAllocator::with(std::alloc::System); + +#[cfg(all( + not(all(feature = "hotpath", feature = "hotpath-alloc")), + feature = "mimalloc", + not(feature = "jemalloc") +))] #[global_allocator] static GLOBAL: rustfs_mimalloc::MiMalloc = rustfs_mimalloc::MiMalloc; +#[cfg(all( + not(all(feature = "hotpath", feature = "hotpath-alloc")), + feature = "jemalloc", + not(feature = "mimalloc"), + not(target_os = "windows") +))] +#[global_allocator] +static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + fn main() { let _hotpath_guard = hotpath::HotpathGuardBuilder::new("main").build(); rustfs::startup_entrypoint::run_process(); } -#[cfg(all(test, feature = "hotpath", feature = "hotpath-alloc", not(target_os = "windows")))] +#[cfg(all( + test, + feature = "hotpath", + feature = "hotpath-alloc", + feature = "mimalloc", + not(feature = "jemalloc"), + not(target_os = "windows") +))] mod tests { #[test] // SAFETY: This test inspects a live allocation pointer with mimalloc's heap diff --git a/rustfs/src/memory_observability.rs b/rustfs/src/memory_observability.rs index 12bd4b3c5..ef2fc349d 100644 --- a/rustfs/src/memory_observability.rs +++ b/rustfs/src/memory_observability.rs @@ -17,6 +17,7 @@ use rustfs_io_metrics::{ record_cpu_usage, record_memory_usage, record_process_memory_split, }; use serde::Serialize; +#[cfg(any(feature = "mimalloc", test))] use serde_json::Value; use std::path::Path; use std::sync::{Arc, Mutex, OnceLock}; @@ -228,6 +229,7 @@ fn read_cgroup_memory_snapshot() -> Option { read_cgroup_v2().or_else(read_cgroup_v1) } +#[cfg(feature = "mimalloc")] fn read_allocator_memory_snapshot() -> Option { let json = rustfs_mimalloc::MiMalloc::stats_json(); if json.is_empty() { @@ -240,6 +242,12 @@ fn read_allocator_memory_snapshot() -> Option { }) } +#[cfg(not(feature = "mimalloc"))] +fn read_allocator_memory_snapshot() -> Option { + None +} + +#[cfg(any(feature = "mimalloc", test))] fn numeric_json_value(value: &Value) -> Option { match value { Value::Number(number) => number @@ -250,6 +258,7 @@ fn numeric_json_value(value: &Value) -> Option { } } +#[cfg(any(feature = "mimalloc", test))] fn numeric_json_field(value: &Value, field: &str) -> Option { match value { Value::Object(fields) => fields @@ -261,6 +270,7 @@ fn numeric_json_field(value: &Value, field: &str) -> Option { } } +#[cfg(any(feature = "mimalloc", test))] fn mimalloc_stat_field(value: &Value, metric: &str, field: &str) -> Option { match value { Value::Object(fields) => { @@ -277,10 +287,12 @@ fn mimalloc_stat_field(value: &Value, metric: &str, field: &str) -> Option } } +#[cfg(any(feature = "mimalloc", test))] fn mimalloc_stat_current(value: &Value, metric: &str) -> Option { mimalloc_stat_field(value, metric, "current") } +#[cfg(any(feature = "mimalloc", test))] fn mimalloc_stat_sum(value: &Value, metrics: &[&str], field: &str) -> Option { metrics .iter() @@ -289,6 +301,7 @@ fn mimalloc_stat_sum(value: &Value, metrics: &[&str], field: &str) -> Option 0) } +#[cfg(any(feature = "mimalloc", test))] fn parse_mimalloc_stats_json(stats_json: &str) -> Option { let value = serde_json::from_str::(stats_json).ok()?; let malloc_metrics = ["malloc_normal", "malloc_huge"]; @@ -545,8 +558,10 @@ mod tests { #[test] fn read_allocator_memory_snapshot_uses_mimalloc_stats_json() { let snapshot = super::read_allocator_memory_snapshot(); - #[cfg(not(target_os = "windows"))] + #[cfg(all(feature = "mimalloc", not(target_os = "windows")))] assert!(snapshot.is_some(), "allocator snapshot should be available on non-Windows"); + #[cfg(not(feature = "mimalloc"))] + assert!(snapshot.is_none(), "allocator snapshot should be absent without mimalloc"); } #[test]