mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-31 01:09:23 +00:00
@@ -1,3 +1,4 @@
|
||||
use madmin::heal_commands::HealResultItem;
|
||||
use std::{cmp::Ordering, env, path::PathBuf, sync::Arc, time::Duration};
|
||||
use tokio::{
|
||||
sync::{
|
||||
@@ -10,7 +11,7 @@ use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{
|
||||
heal_commands::{HealOpts, HealResultItem},
|
||||
heal_commands::HealOpts,
|
||||
heal_ops::{new_bg_heal_sequence, HealSequence},
|
||||
};
|
||||
use crate::heal::error::ERR_RETRY_HEALING;
|
||||
|
||||
@@ -62,7 +62,7 @@ use crate::{
|
||||
store_api::{FileInfo, ObjectInfo},
|
||||
};
|
||||
|
||||
const _DATA_SCANNER_SLEEP_PER_FOLDER: Duration = Duration::from_millis(1); // Time to wait between folders.
|
||||
const DATA_SCANNER_SLEEP_PER_FOLDER: Duration = Duration::from_millis(1); // Time to wait between folders.
|
||||
const DATA_USAGE_UPDATE_DIR_CYCLES: u32 = 16; // Visit all folders every n cycles.
|
||||
const DATA_SCANNER_COMPACT_LEAST_OBJECT: u64 = 500; // Compact when there are less than this many objects in a branch.
|
||||
const DATA_SCANNER_COMPACT_AT_CHILDREN: u64 = 10000; // Compact when there are this many children in a branch.
|
||||
@@ -73,7 +73,6 @@ const DATA_SCANNER_START_DELAY: Duration = Duration::from_secs(60); // Time to w
|
||||
pub const HEAL_DELETE_DANGLING: bool = true;
|
||||
const HEAL_OBJECT_SELECT_PROB: u64 = 1024; // Overall probability of a file being scanned; one in n.
|
||||
|
||||
// static SCANNER_SLEEPER: () = new_dynamic_sleeper(2, Duration::from_secs(1), true); // Keep defaults same as config defaults
|
||||
static SCANNER_CYCLE: AtomicU64 = AtomicU64::new(DATA_SCANNER_START_DELAY.as_secs());
|
||||
static _SCANNER_IDLE_MODE: AtomicU32 = AtomicU32::new(0); // default is throttled when idle
|
||||
static SCANNER_EXCESS_OBJECT_VERSIONS: AtomicU64 = AtomicU64::new(100);
|
||||
@@ -81,9 +80,67 @@ static SCANNER_EXCESS_OBJECT_VERSIONS_TOTAL_SIZE: AtomicU64 = AtomicU64::new(102
|
||||
static SCANNER_EXCESS_FOLDERS: AtomicU64 = AtomicU64::new(50_000);
|
||||
|
||||
lazy_static! {
|
||||
static ref SCANNER_SLEEPER: RwLock<DynamicSleeper> = RwLock::new(new_dynamic_sleeper(2.0, Duration::from_secs(1), true));
|
||||
pub static ref globalHealConfig: Arc<RwLock<Config>> = Arc::new(RwLock::new(Config::default()));
|
||||
}
|
||||
|
||||
struct DynamicSleeper {
|
||||
factor: f64,
|
||||
max_sleep: Duration,
|
||||
min_sleep: Duration,
|
||||
_is_scanner: bool,
|
||||
}
|
||||
|
||||
type TimerFn = Pin<Box<dyn Future<Output = ()> + Send>>;
|
||||
impl DynamicSleeper {
|
||||
fn timer() -> TimerFn {
|
||||
let t = SystemTime::now();
|
||||
Box::pin(async move {
|
||||
let done_at = SystemTime::now().duration_since(t).unwrap_or_default();
|
||||
SCANNER_SLEEPER.read().await.sleep(done_at).await;
|
||||
})
|
||||
}
|
||||
|
||||
async fn sleep(&self, base: Duration) {
|
||||
let (min_wait, max_wait) = (self.min_sleep, self.max_sleep);
|
||||
let factor = self.factor;
|
||||
|
||||
let want_sleep = {
|
||||
let tmp = base.mul_f64(factor);
|
||||
if tmp < min_wait {
|
||||
return;
|
||||
}
|
||||
|
||||
if max_wait > Duration::from_secs(0) && tmp > max_wait {
|
||||
max_wait
|
||||
} else {
|
||||
tmp
|
||||
}
|
||||
};
|
||||
sleep(want_sleep).await;
|
||||
}
|
||||
|
||||
fn _update(&mut self, factor: f64, max_wait: Duration) -> Result<()> {
|
||||
if (self.factor - factor).abs() < 1e-10 && self.max_sleep == max_wait {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.factor = factor;
|
||||
self.max_sleep = max_wait;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_dynamic_sleeper(factor: f64, max_wait: Duration, is_scanner: bool) -> DynamicSleeper {
|
||||
DynamicSleeper {
|
||||
factor,
|
||||
max_sleep: max_wait,
|
||||
min_sleep: Duration::from_micros(100),
|
||||
_is_scanner: is_scanner,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn init_data_scanner() {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
@@ -457,6 +514,7 @@ struct CachedFolder {
|
||||
pub type GetSizeFn =
|
||||
Box<dyn Fn(&ScannerItem) -> Pin<Box<dyn Future<Output = Result<SizeSummary>> + Send>> + Send + Sync + 'static>;
|
||||
pub type UpdateCurrentPathFn = Arc<dyn Fn(&str) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static>;
|
||||
pub type ShouldSleepFn = Option<Arc<dyn Fn() -> bool + Send + Sync + 'static>>;
|
||||
|
||||
struct FolderScanner {
|
||||
root: String,
|
||||
@@ -474,6 +532,7 @@ struct FolderScanner {
|
||||
update_current_path: UpdateCurrentPathFn,
|
||||
skip_heal: AtomicBool,
|
||||
drive: LocalDrive,
|
||||
we_sleep: ShouldSleepFn,
|
||||
}
|
||||
|
||||
impl FolderScanner {
|
||||
@@ -514,6 +573,12 @@ impl FolderScanner {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(should_sleep) = &self.we_sleep {
|
||||
if should_sleep() {
|
||||
SCANNER_SLEEPER.read().await.sleep(DATA_SCANNER_SLEEP_PER_FOLDER).await;
|
||||
}
|
||||
}
|
||||
|
||||
let mut existing_folders = Vec::new();
|
||||
let mut new_folders = Vec::new();
|
||||
let mut found_objects: bool = false;
|
||||
@@ -553,6 +618,16 @@ impl FolderScanner {
|
||||
continue;
|
||||
}
|
||||
|
||||
let _wait = if let Some(should_sleep) = &self.we_sleep {
|
||||
if should_sleep() {
|
||||
DynamicSleeper::timer()
|
||||
} else {
|
||||
Box::pin(async {})
|
||||
}
|
||||
} else {
|
||||
Box::pin(async {})
|
||||
};
|
||||
|
||||
let mut item = ScannerItem {
|
||||
path: Path::new(&self.root).join(&ent_name).to_string_lossy().to_string(),
|
||||
bucket,
|
||||
@@ -1001,6 +1076,7 @@ pub async fn scan_data_folder(
|
||||
cache: &DataUsageCache,
|
||||
get_size_fn: GetSizeFn,
|
||||
heal_scan_mode: HealScanMode,
|
||||
should_sleep: ShouldSleepFn,
|
||||
) -> Result<DataUsageCache> {
|
||||
if cache.info.name.is_empty() || cache.info.name == DATA_USAGE_ROOT {
|
||||
return Err(Error::from_string("internal error: root scan attempted"));
|
||||
@@ -1029,6 +1105,7 @@ pub async fn scan_data_folder(
|
||||
disks_quorum: disks.len() / 2,
|
||||
skip_heal,
|
||||
drive: drive.clone(),
|
||||
we_sleep: should_sleep,
|
||||
};
|
||||
|
||||
if *GLOBAL_IsErasure.read().await || !cache.info.skip_healing {
|
||||
|
||||
@@ -24,7 +24,6 @@ use crate::{
|
||||
use super::{background_heal_ops::get_local_disks_to_heal, heal_ops::BG_HEALING_UUID};
|
||||
|
||||
pub type HealScanMode = usize;
|
||||
pub type HealItemType = String;
|
||||
|
||||
pub const HEAL_UNKNOWN_SCAN: HealScanMode = 0;
|
||||
pub const HEAL_NORMAL_SCAN: HealScanMode = 1;
|
||||
@@ -66,49 +65,6 @@ pub struct HealOpts {
|
||||
pub set: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct HealDriveInfo {
|
||||
pub uuid: String,
|
||||
pub endpoint: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Infos {
|
||||
#[serde(rename = "drives")]
|
||||
pub drives: Vec<HealDriveInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct HealResultItem {
|
||||
#[serde(rename = "resultId")]
|
||||
pub result_index: usize,
|
||||
#[serde(rename = "type")]
|
||||
pub heal_item_type: HealItemType,
|
||||
#[serde(rename = "bucket")]
|
||||
pub bucket: String,
|
||||
#[serde(rename = "object")]
|
||||
pub object: String,
|
||||
#[serde(rename = "versionId")]
|
||||
pub version_id: String,
|
||||
#[serde(rename = "detail")]
|
||||
pub detail: String,
|
||||
#[serde(rename = "parityBlocks")]
|
||||
pub parity_blocks: usize,
|
||||
#[serde(rename = "dataBlocks")]
|
||||
pub data_blocks: usize,
|
||||
#[serde(rename = "diskCount")]
|
||||
pub disk_count: usize,
|
||||
#[serde(rename = "setCount")]
|
||||
pub set_count: usize,
|
||||
#[serde(rename = "before")]
|
||||
pub before: Infos,
|
||||
#[serde(rename = "after")]
|
||||
pub after: Infos,
|
||||
#[serde(rename = "objectSize")]
|
||||
pub object_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct HealStartSuccess {
|
||||
#[serde(rename = "clientToken")]
|
||||
|
||||
@@ -2,19 +2,14 @@ use super::{
|
||||
background_heal_ops::HealTask,
|
||||
data_scanner::HEAL_DELETE_DANGLING,
|
||||
error::ERR_SKIP_FILE,
|
||||
heal_commands::{
|
||||
HealItemType, HealOpts, HealResultItem, HealScanMode, HealStopSuccess, HealingTracker, HEAL_ITEM_BUCKET_METADATA,
|
||||
},
|
||||
heal_commands::{HealOpts, HealScanMode, HealStopSuccess, HealingDisk, HealingTracker, HEAL_ITEM_BUCKET_METADATA},
|
||||
};
|
||||
use crate::store_api::StorageAPI;
|
||||
use crate::{
|
||||
config::common::CONFIG_PREFIX,
|
||||
disk::RUSTFS_META_BUCKET,
|
||||
global::GLOBAL_BackgroundHealRoutine,
|
||||
heal::{
|
||||
error::ERR_HEAL_STOP_SIGNALLED,
|
||||
heal_commands::{HealDriveInfo, DRIVE_STATE_OK},
|
||||
},
|
||||
heal::{error::ERR_HEAL_STOP_SIGNALLED, heal_commands::DRIVE_STATE_OK},
|
||||
};
|
||||
use crate::{
|
||||
disk::{endpoint::Endpoint, MetaCacheEntry},
|
||||
@@ -32,6 +27,7 @@ use crate::{
|
||||
use chrono::Utc;
|
||||
use futures::join;
|
||||
use lazy_static::lazy_static;
|
||||
use madmin::heal_commands::{HealDriveInfo, HealItemType, HealResultItem};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
|
||||
Reference in New Issue
Block a user