forked from Deuxfleurs/garage
- [x] New background worker trait - [x] Adapt all current workers to use new API - [x] Command to list currently running workers, and whether they are active, idle, or dead - [x] Error reporting - Optimizations - [x] Merkle updater: several items per iteration - [ ] Use `tokio::task::spawn_blocking` where appropriate so that CPU-intensive tasks don't block other things going on - scrub: - [x] have only one worker with a channel to start/pause/cancel - [x] automatic scrub - [x] ability to view and change tranquility from CLI - [x] persistence of a few info - [ ] Testing Co-authored-by: Alex Auvolat <alex@adnab.me> Reviewed-on: Deuxfleurs/garage#332 Co-authored-by: Alex <alex@adnab.me> Co-committed-by: Alex <alex@adnab.me>
78 lines
2.2 KiB
Rust
78 lines
2.2 KiB
Rust
use std::collections::VecDeque;
|
|
use std::time::{Duration, Instant};
|
|
|
|
use tokio::time::sleep;
|
|
|
|
use crate::background::WorkerState;
|
|
|
|
/// A tranquilizer is a helper object that is used to make
|
|
/// background operations not take up too much time.
|
|
///
|
|
/// Background operations are done in a loop that does the following:
|
|
/// - do one step of the background process
|
|
/// - tranquilize, i.e. wait some time to not overload the system
|
|
///
|
|
/// The tranquilizer observes how long the steps take, and keeps
|
|
/// in memory a number of observations. The tranquilize operation
|
|
/// simply sleeps k * avg(observed step times), where k is
|
|
/// the tranquility factor. For instance with a tranquility of 2,
|
|
/// the tranquilizer will sleep on average 2 units of time for every
|
|
/// 1 unit of time spent doing the background task.
|
|
pub struct Tranquilizer {
|
|
n_observations: usize,
|
|
observations: VecDeque<Duration>,
|
|
sum_observations: Duration,
|
|
last_step_begin: Instant,
|
|
}
|
|
|
|
impl Tranquilizer {
|
|
pub fn new(n_observations: usize) -> Self {
|
|
Self {
|
|
n_observations,
|
|
observations: VecDeque::with_capacity(n_observations + 1),
|
|
sum_observations: Duration::ZERO,
|
|
last_step_begin: Instant::now(),
|
|
}
|
|
}
|
|
|
|
fn tranquilize_internal(&mut self, tranquility: u32) -> Option<Duration> {
|
|
let observation = Instant::now() - self.last_step_begin;
|
|
|
|
self.observations.push_back(observation);
|
|
self.sum_observations += observation;
|
|
|
|
while self.observations.len() > self.n_observations {
|
|
self.sum_observations -= self.observations.pop_front().unwrap();
|
|
}
|
|
|
|
if !self.observations.is_empty() {
|
|
let delay = (tranquility * self.sum_observations) / (self.observations.len() as u32);
|
|
Some(delay)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub async fn tranquilize(&mut self, tranquility: u32) {
|
|
if let Some(delay) = self.tranquilize_internal(tranquility) {
|
|
sleep(delay).await;
|
|
self.reset();
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn tranquilize_worker(&mut self, tranquility: u32) -> WorkerState {
|
|
match self.tranquilize_internal(tranquility) {
|
|
Some(delay) => WorkerState::Throttled(delay.as_secs_f32()),
|
|
None => WorkerState::Busy,
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.last_step_begin = Instant::now();
|
|
}
|
|
|
|
pub fn clear(&mut self) {
|
|
self.observations.clear();
|
|
}
|
|
}
|