Skip to content

Commit fe1df52

Browse files
committed
fix(control-plane): debounce ingester pool rebalances
1 parent 30f075b commit fe1df52

6 files changed

Lines changed: 157 additions & 241 deletions

File tree

quickwit/quickwit-cluster/src/change.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ async fn try_new_node(
328328
#[cfg(any(test, feature = "testsuite"))]
329329
pub mod for_test {
330330
use quickwit_config::GrpcConfig;
331-
use tokio::sync::mpsc;
332331

333332
use super::*;
334333

@@ -337,21 +336,6 @@ pub mod for_test {
337336
ChannelFactory::for_grpc(&GrpcConfig::default())
338337
.expect("plaintext channel factory should build")
339338
}
340-
341-
pub struct ClusterChangeStreamForTest {
342-
pub cluster_change_rx: ClusterChangeStream,
343-
pub cluster_change_tx: mpsc::UnboundedSender<ClusterChange>,
344-
}
345-
346-
impl Default for ClusterChangeStreamForTest {
347-
fn default() -> Self {
348-
let (cluster_change_rx, cluster_change_tx) = ClusterChangeStream::new_unbounded();
349-
Self {
350-
cluster_change_rx,
351-
cluster_change_tx,
352-
}
353-
}
354-
}
355339
}
356340

357341
#[cfg(test)]

quickwit/quickwit-common/src/tower/pool.rs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@ use std::hash::Hash;
2121
use std::sync::{Arc, RwLock};
2222

2323
use futures::{Stream, StreamExt};
24+
use tokio::sync::watch;
2425

2526
use super::Change;
2627

2728
/// A pool of `V` values identified by `K` keys. The pool can be updated manually by calling the
2829
/// `add/remove` methods or by listening to a stream of changes.
2930
pub struct Pool<K, V> {
3031
pool: Arc<RwLock<HashMap<K, V>>>,
32+
/// Publishes the pool generation after each mutation.
33+
pub generation_tx: watch::Sender<u64>,
34+
/// Receives pool generation changes.
35+
pub generation_rx: watch::Receiver<u64>,
3136
}
3237

3338
impl<K, V> fmt::Debug for Pool<K, V>
@@ -44,16 +49,22 @@ impl<K, V> Clone for Pool<K, V> {
4449
fn clone(&self) -> Self {
4550
Self {
4651
pool: self.pool.clone(),
52+
generation_tx: self.generation_tx.clone(),
53+
generation_rx: self.generation_rx.clone(),
4754
}
4855
}
4956
}
5057

5158
impl<K, V> Default for Pool<K, V>
52-
where K: Eq + PartialEq + Hash
59+
where
60+
K: Eq + PartialEq + Hash,
5361
{
5462
fn default() -> Self {
63+
let (generation_tx, generation_rx) = watch::channel(1);
5564
Self {
5665
pool: Arc::new(RwLock::new(HashMap::default())),
66+
generation_tx,
67+
generation_rx,
5768
}
5869
}
5970
}
@@ -86,6 +97,19 @@ where
8697
tokio::spawn(future);
8798
}
8899

100+
/// Returns the current pool generation.
101+
pub fn generation(&self) -> u64 {
102+
*self.generation_rx.borrow()
103+
}
104+
105+
fn increment_generation(&self) {
106+
self.generation_tx.send_modify(|generation| {
107+
*generation = generation
108+
.checked_add(1)
109+
.expect("pool generation should not overflow")
110+
});
111+
}
112+
89113
/// Returns whether the pool is empty.
90114
pub fn is_empty(&self) -> bool {
91115
self.pool
@@ -165,12 +189,12 @@ where
165189
}
166190

167191
/// Finds a key in the pool that satisfies the given predicate.
168-
pub fn find(&self, func: impl Fn(&K, &V) -> bool) -> Option<(K, V)> {
192+
pub fn find(&self, predicate_fn: impl Fn(&K, &V) -> bool) -> Option<(K, V)> {
169193
self.pool
170194
.read()
171195
.expect("lock should not be poisoned")
172196
.iter()
173-
.find(|(key, value)| func(key, value))
197+
.find(|(key, value)| predicate_fn(key, value))
174198
.map(|(key, value)| (key.clone(), value.clone()))
175199
}
176200

@@ -180,24 +204,35 @@ where
180204
.write()
181205
.expect("lock should not be poisoned")
182206
.insert(key, service);
207+
self.increment_generation();
183208
}
184209

185210
/// Removes a value from the pool.
186211
fn remove(&self, key: &K) {
187-
self.pool
212+
let removed = self
213+
.pool
188214
.write()
189215
.expect("lock should not be poisoned")
190216
.remove(key);
217+
if removed.is_some() {
218+
self.increment_generation();
219+
}
191220
}
192221
}
193222

194223
impl<K, V> FromIterator<(K, V)> for Pool<K, V>
195-
where K: Eq + PartialEq + Hash
224+
where
225+
K: Eq + PartialEq + Hash,
196226
{
197227
fn from_iter<I>(iter: I) -> Self
198-
where I: IntoIterator<Item = (K, V)> {
228+
where
229+
I: IntoIterator<Item = (K, V)>,
230+
{
231+
let (generation_tx, generation_rx) = watch::channel(0);
199232
Self {
200233
pool: Arc::new(RwLock::new(HashMap::from_iter(iter))),
234+
generation_tx,
235+
generation_rx,
201236
}
202237
}
203238
}
@@ -210,6 +245,32 @@ mod tests {
210245

211246
use super::*;
212247

248+
#[tokio::test]
249+
async fn test_pool_generation() {
250+
let pool = Pool::default();
251+
let mut generation_rx = pool.generation_rx.clone();
252+
253+
assert_eq!(pool.generation(), 0);
254+
255+
pool.insert(1, 11);
256+
generation_rx.changed().await.unwrap();
257+
assert_eq!(*generation_rx.borrow_and_update(), 1);
258+
assert_eq!(pool.get(&1), Some(11));
259+
260+
pool.insert(1, 12);
261+
generation_rx.changed().await.unwrap();
262+
assert_eq!(*generation_rx.borrow_and_update(), 2);
263+
assert_eq!(pool.get(&1), Some(12));
264+
265+
pool.remove(&2);
266+
assert_eq!(pool.generation(), 2);
267+
268+
pool.remove(&1);
269+
generation_rx.changed().await.unwrap();
270+
assert_eq!(*generation_rx.borrow_and_update(), 3);
271+
assert!(!pool.contains_key(&1));
272+
}
273+
213274
#[tokio::test]
214275
async fn test_pool() {
215276
let (change_stream_tx, change_stream_rx) = tokio::sync::mpsc::channel(10);

0 commit comments

Comments
 (0)