mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-13 08:36:54 +00:00
fix(scanner): publish partial usage for compacted scans (#3277)
* fix(scanner): publish partial usage for compacted scans * fix(scanner): publish first partial usage immediately * fix(scanner): skip startup delay for cold usage cache * fix(scanner): tighten cold usage publish gate --------- Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com> Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
@@ -1024,14 +1024,18 @@ impl FolderScanner {
|
||||
/// Send update if enough time has passed
|
||||
/// Should be called on a regular basis when the new_cache contains more recent total than previously.
|
||||
/// May or may not send an update upstream.
|
||||
pub async fn send_update(&mut self) {
|
||||
// Send at most an update every minute.
|
||||
fn should_send_update(&self) -> bool {
|
||||
if self.updates.is_none() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let elapsed = self.last_update.elapsed().unwrap_or(Duration::from_secs(0));
|
||||
if elapsed < Duration::from_secs(60) {
|
||||
elapsed >= Duration::from_secs(60)
|
||||
}
|
||||
|
||||
pub async fn send_update(&mut self) {
|
||||
// Send at most an update every minute.
|
||||
if !self.should_send_update() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1046,6 +1050,15 @@ impl FolderScanner {
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_update_for_entry(&mut self, hash: &DataUsageHash, parent: &Option<DataUsageHash>, entry: &DataUsageEntry) {
|
||||
if !self.should_send_update() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.update_cache.replace_hashed(hash, parent, entry);
|
||||
self.send_update().await;
|
||||
}
|
||||
|
||||
/// Scan a folder recursively
|
||||
/// Files found in the folders will be added to new_cache.
|
||||
#[allow(clippy::never_loop)]
|
||||
@@ -1300,6 +1313,7 @@ impl FolderScanner {
|
||||
}
|
||||
|
||||
if should_yield_after_object(object_count, yield_every_objects) {
|
||||
self.send_update_for_entry(&this_hash, &folder.parent, into).await;
|
||||
let yield_start = Instant::now();
|
||||
tokio::task::yield_now().await;
|
||||
global_metrics().record_scanner_yield(yield_start.elapsed());
|
||||
@@ -1445,6 +1459,7 @@ impl FolderScanner {
|
||||
let fut = Box::pin(self.scan_folder(ctx.clone(), folder_item.clone(), into));
|
||||
fut.await.map_err(|e| ScannerError::Other(e.to_string()))?;
|
||||
self.record_scan_resume_hint(&folder_item.name);
|
||||
self.send_update_for_entry(&this_hash, &folder.parent, into).await;
|
||||
tokio::task::yield_now().await;
|
||||
} else {
|
||||
let mut dst = DataUsageEntry::default();
|
||||
@@ -1694,6 +1709,7 @@ impl FolderScanner {
|
||||
// In compacted mode child totals are accumulated directly into the parent entry.
|
||||
let fut = Box::pin(self.scan_folder(ctx.clone(), folder_item.clone(), into));
|
||||
fut.await.map_err(|e| ScannerError::Other(e.to_string()))?;
|
||||
self.send_update_for_entry(&this_hash, &folder.parent, into).await;
|
||||
tokio::task::yield_now().await;
|
||||
} else {
|
||||
let mut dst = DataUsageEntry::default();
|
||||
@@ -2601,6 +2617,48 @@ mod tests {
|
||||
assert!(budget.token().is_cancelled());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_scan_folder_compacted_parent_sends_partial_update() {
|
||||
let (mut scanner, temp_dir) = build_test_scanner().await;
|
||||
let _guard = TestGuard::new(60, 100, &mut scanner, temp_dir.clone());
|
||||
|
||||
let bucket_dir = temp_dir.join("bucket");
|
||||
tokio::fs::create_dir_all(bucket_dir.join("child"))
|
||||
.await
|
||||
.expect("failed to create child directory");
|
||||
|
||||
scanner.old_cache.info.name = "bucket".to_string();
|
||||
scanner.new_cache.info.name = "bucket".to_string();
|
||||
scanner.update_cache.info.name = "bucket".to_string();
|
||||
scanner.last_update = SystemTime::UNIX_EPOCH;
|
||||
|
||||
let (tx, mut rx) = mpsc::channel(1);
|
||||
scanner.updates = Some(tx);
|
||||
|
||||
let folder = CachedFolder {
|
||||
name: "bucket".to_string(),
|
||||
parent: None,
|
||||
object_heal_prob_div: 1,
|
||||
};
|
||||
let mut into = DataUsageEntry {
|
||||
compacted: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
scanner
|
||||
.scan_folder(CancellationToken::new(), folder, &mut into)
|
||||
.await
|
||||
.expect("compacted scan should finish successfully");
|
||||
|
||||
let update = tokio::time::timeout(Duration::from_secs(1), rx.recv())
|
||||
.await
|
||||
.expect("compacted scan should send a partial update")
|
||||
.expect("partial update channel should remain open");
|
||||
|
||||
assert!(update.compacted, "partial update should preserve compacted state");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_scan_data_folder_returns_partial_cache_on_budget_cancel() {
|
||||
|
||||
Reference in New Issue
Block a user