mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 13:16:28 +00:00
feat(lifecycle): improve ILM compatibility and scanner runtime config (#2534)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> Co-authored-by: cxymds <Cxymds@qq.com>
This commit is contained in:
@@ -22,10 +22,8 @@ use crate::{DataUsageInfo, ScannerError};
|
||||
use chrono::{DateTime, Utc};
|
||||
use rustfs_common::heal_channel::HealScanMode;
|
||||
use rustfs_common::metrics::{CurrentCycle, Metric, Metrics, emit_scan_cycle_complete, global_metrics};
|
||||
use rustfs_config::DEFAULT_SCANNER_SPEED;
|
||||
use rustfs_config::ENV_SCANNER_SPEED;
|
||||
use rustfs_config::ENV_SCANNER_START_DELAY_SECS;
|
||||
use rustfs_config::ScannerSpeed;
|
||||
use rustfs_config::{DEFAULT_SCANNER_SPEED, ENV_SCANNER_CYCLE, ENV_SCANNER_SPEED, ENV_SCANNER_START_DELAY_SECS};
|
||||
use rustfs_ecstore::StorageAPI as _;
|
||||
use rustfs_ecstore::config::com::{read_config, save_config};
|
||||
use rustfs_ecstore::disk::RUSTFS_META_BUCKET;
|
||||
@@ -40,11 +38,15 @@ use tracing::{debug, error, info, instrument, warn};
|
||||
|
||||
const ENV_SCANNER_START_DELAY_SECS_DEPRECATED: &str = "RUSTFS_DATA_SCANNER_START_DELAY_SECS";
|
||||
|
||||
/// Returns the base cycle interval. If `RUSTFS_SCANNER_START_DELAY_SECS`
|
||||
/// is set (or `RUSTFS_DATA_SCANNER_START_DELAY_SECS` as deprecated alias),
|
||||
/// it takes precedence; otherwise the value is derived from the
|
||||
/// `RUSTFS_SCANNER_SPEED` preset.
|
||||
/// Returns the base cycle interval.
|
||||
/// Priority order:
|
||||
/// 1. RUSTFS_SCANNER_CYCLE (if set, overrides everything)
|
||||
/// 2. RUSTFS_SCANNER_START_DELAY_SECS (for backward compatibility)
|
||||
/// 3. RUSTFS_SCANNER_SPEED preset
|
||||
fn cycle_interval() -> Duration {
|
||||
if let Some(secs) = rustfs_utils::get_env_opt_u64(ENV_SCANNER_CYCLE) {
|
||||
return Duration::from_secs(secs);
|
||||
}
|
||||
if let Some(secs) = scanner_start_delay_secs() {
|
||||
return Duration::from_secs(secs);
|
||||
}
|
||||
@@ -181,6 +183,7 @@ fn get_lock_acquire_timeout() -> Duration {
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn run_data_scanner_cycle(ctx: &CancellationToken, storeapi: &Arc<ECStore>, cycle_info: &mut CurrentCycle) {
|
||||
SCANNER_SLEEPER.refresh_from_env();
|
||||
info!("Start run data scanner cycle");
|
||||
cycle_info.current = cycle_info.next;
|
||||
let now = Instant::now();
|
||||
@@ -356,6 +359,7 @@ pub async fn store_data_usage_in_backend(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serial_test::serial;
|
||||
use temp_env::{with_var, with_var_unset};
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
@@ -376,6 +380,42 @@ mod tests {
|
||||
assert!(delay <= Duration::from_secs(132));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_cycle_interval_prefers_explicit_cycle_override() {
|
||||
with_var(ENV_SCANNER_SPEED, Some("slowest"), || {
|
||||
with_var(ENV_SCANNER_CYCLE, Some("42"), || {
|
||||
assert_eq!(cycle_interval(), Duration::from_secs(42));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_cycle_interval_supports_minio_speed_alias() {
|
||||
with_var_unset(ENV_SCANNER_SPEED, || {
|
||||
with_var_unset(ENV_SCANNER_CYCLE, || {
|
||||
with_var_unset(ENV_SCANNER_START_DELAY_SECS, || {
|
||||
with_var("MINIO_SCANNER_SPEED", Some("slowest"), || {
|
||||
assert_eq!(cycle_interval(), Duration::from_secs(30 * 60));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_cycle_interval_supports_minio_cycle_alias() {
|
||||
with_var_unset(ENV_SCANNER_CYCLE, || {
|
||||
with_var_unset(ENV_SCANNER_START_DELAY_SECS, || {
|
||||
with_var("MINIO_SCANNER_CYCLE", Some("90"), || {
|
||||
assert_eq!(cycle_interval(), Duration::from_secs(90));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_randomized_cycle_delay_handles_small_start_delay() {
|
||||
|
||||
@@ -30,7 +30,7 @@ use rustfs_common::heal_channel::{
|
||||
};
|
||||
use rustfs_common::metrics::{IlmAction, Metric, Metrics, UpdateCurrentPathFn, current_path_updater};
|
||||
use rustfs_ecstore::bucket::lifecycle::bucket_lifecycle_audit::LcEventSrc;
|
||||
use rustfs_ecstore::bucket::lifecycle::bucket_lifecycle_ops::apply_expiry_rule;
|
||||
use rustfs_ecstore::bucket::lifecycle::bucket_lifecycle_ops::{GLOBAL_ExpiryState, apply_expiry_rule};
|
||||
use rustfs_ecstore::bucket::lifecycle::evaluator::Evaluator;
|
||||
use rustfs_ecstore::bucket::lifecycle::{
|
||||
bucket_lifecycle_ops::apply_transition_rule,
|
||||
@@ -444,8 +444,14 @@ impl ScannerItem {
|
||||
}
|
||||
}
|
||||
|
||||
if !to_delete_objs.is_empty() {
|
||||
// TODO: enqueueNoncurrentVersions
|
||||
if !to_delete_objs.is_empty()
|
||||
&& let Some(event) = noncurrent_events.first().cloned()
|
||||
{
|
||||
GLOBAL_ExpiryState
|
||||
.write()
|
||||
.await
|
||||
.enqueue_by_newer_noncurrent(&self.bucket, to_delete_objs, event)
|
||||
.await;
|
||||
}
|
||||
self.alert_excessive_versions(remaining_versions, cumulative_size);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@ use tokio::time::Duration;
|
||||
|
||||
const MIN_SLEEP: Duration = Duration::from_millis(1);
|
||||
|
||||
fn scanner_env_config() -> (ScannerSpeed, bool) {
|
||||
let speed_str = rustfs_utils::get_env_str(ENV_SCANNER_SPEED, DEFAULT_SCANNER_SPEED);
|
||||
let speed = ScannerSpeed::from_env_str(&speed_str);
|
||||
let idle_mode = rustfs_utils::get_env_bool(ENV_SCANNER_IDLE_MODE, DEFAULT_SCANNER_IDLE_MODE);
|
||||
(speed, idle_mode)
|
||||
}
|
||||
|
||||
/// When `true` (default), the scanner throttles itself between operations.
|
||||
/// When `false`, all sleeps are skipped and the scanner runs at full speed.
|
||||
pub static SCANNER_IDLE_MODE: AtomicBool = AtomicBool::new(DEFAULT_SCANNER_IDLE_MODE);
|
||||
@@ -28,10 +35,7 @@ pub static SCANNER_IDLE_MODE: AtomicBool = AtomicBool::new(DEFAULT_SCANNER_IDLE_
|
||||
/// Global scanner sleeper initialized from the `RUSTFS_SCANNER_SPEED` and
|
||||
/// `RUSTFS_SCANNER_IDLE_MODE` environment variables.
|
||||
pub static SCANNER_SLEEPER: LazyLock<DynamicSleeper> = LazyLock::new(|| {
|
||||
let speed_str = rustfs_utils::get_env_str(ENV_SCANNER_SPEED, DEFAULT_SCANNER_SPEED);
|
||||
let speed = ScannerSpeed::from_env_str(&speed_str);
|
||||
|
||||
let idle_mode = rustfs_utils::get_env_bool(ENV_SCANNER_IDLE_MODE, DEFAULT_SCANNER_IDLE_MODE);
|
||||
let (speed, idle_mode) = scanner_env_config();
|
||||
SCANNER_IDLE_MODE.store(idle_mode, Ordering::Relaxed);
|
||||
|
||||
DynamicSleeper::new(speed)
|
||||
@@ -104,6 +108,13 @@ impl DynamicSleeper {
|
||||
let mut m = self.inner.max_sleep.write().unwrap_or_else(|e| e.into_inner());
|
||||
*m = speed.max_sleep();
|
||||
}
|
||||
|
||||
/// Reload speed and idle-mode settings from the current environment.
|
||||
pub fn refresh_from_env(&self) {
|
||||
let (speed, idle_mode) = scanner_env_config();
|
||||
self.update(speed);
|
||||
SCANNER_IDLE_MODE.store(idle_mode, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// A timer returned by [`DynamicSleeper::timer`]. Records the instant it
|
||||
@@ -138,6 +149,7 @@ impl SleepTimer {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serial_test::serial;
|
||||
use temp_env::with_var;
|
||||
|
||||
#[test]
|
||||
fn test_scanner_speed_presets() {
|
||||
@@ -166,6 +178,26 @@ mod tests {
|
||||
assert_eq!(max_sleep, Duration::from_secs(15));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_refresh_from_env_applies_speed_and_idle_mode_for_next_cycle() {
|
||||
let prev_mode = SCANNER_IDLE_MODE.load(Ordering::Relaxed);
|
||||
SCANNER_IDLE_MODE.store(true, Ordering::Relaxed);
|
||||
|
||||
let s = DynamicSleeper::new(ScannerSpeed::Fastest);
|
||||
with_var(ENV_SCANNER_SPEED, Some("slow"), || {
|
||||
with_var(ENV_SCANNER_IDLE_MODE, Some("false"), || {
|
||||
s.refresh_from_env();
|
||||
let (factor, max_sleep) = s.read_params();
|
||||
assert_eq!(factor, 10.0);
|
||||
assert_eq!(max_sleep, Duration::from_secs(15));
|
||||
assert!(!SCANNER_IDLE_MODE.load(Ordering::Relaxed));
|
||||
});
|
||||
});
|
||||
|
||||
SCANNER_IDLE_MODE.store(prev_mode, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[tokio::test(start_paused = true)]
|
||||
#[serial]
|
||||
async fn test_fastest_never_sleeps() {
|
||||
|
||||
Reference in New Issue
Block a user