aboutsummaryrefslogtreecommitdiff
path: root/src/util/tranquilizer.rs
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2021-11-03 18:28:43 +0100
committerAlex Auvolat <alex@adnab.me>2021-11-04 13:26:59 +0100
commit2090a6187f7d106e0641bed4cac145ad5184995d (patch)
tree822dd5ff1adaaf4b1c4384b2b3604054f378770b /src/util/tranquilizer.rs
parent6f13d083ab188060d2a2dc5f619070a445fe61ba (diff)
downloadgarage-2090a6187f7d106e0641bed4cac145ad5184995d.tar.gz
garage-2090a6187f7d106e0641bed4cac145ad5184995d.zip
Add tranquilizer mechanism to improve on token bucket mechanismtranquility
Diffstat (limited to 'src/util/tranquilizer.rs')
-rw-r--r--src/util/tranquilizer.rs57
1 files changed, 57 insertions, 0 deletions
diff --git a/src/util/tranquilizer.rs b/src/util/tranquilizer.rs
new file mode 100644
index 00000000..28711387
--- /dev/null
+++ b/src/util/tranquilizer.rs
@@ -0,0 +1,57 @@
+use std::collections::VecDeque;
+use std::time::{Duration, Instant};
+
+use tokio::time::sleep;
+
+/// 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(),
+ }
+ }
+
+ pub async fn tranquilize(&mut self, tranquility: u32) {
+ 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);
+ sleep(delay).await;
+ }
+
+ self.reset();
+ }
+
+ pub fn reset(&mut self) {
+ self.last_step_begin = Instant::now();
+ }
+}