pub struct Group<K, V, S = RandomState> { /* private fields */ }Expand description
A namespace that coalesces concurrent work by key.
Different keys run independently. A group neither caches completed results nor limits overall concurrency.
Implementations§
Source§impl<K, V> Group<K, V, RandomState>
impl<K, V> Group<K, V, RandomState>
Source§impl<K, V, S> Group<K, V, S>
impl<K, V, S> Group<K, V, S>
Sourcepub fn with_hasher(hasher: S) -> Self
pub fn with_hasher(hasher: S) -> Self
Creates a new Group with the given hasher.
Sourcepub async fn work<F>(&self, key: K, func: F) -> Vwhere
F: AsyncFnOnce() -> V,
pub async fn work<F>(&self, key: K, func: F) -> Vwhere
F: AsyncFnOnce() -> V,
Executes and returns the results of the given function, making sure that only one execution is in-flight for a given key at a time.
If a duplicate comes in, the duplicate caller waits for the original to complete and receives the same results.
If the computation is cancelled or panics, another caller waiting for the same key may retry it.
Once the function completes, the key, if not forgotten, is removed from the group,
allowing future calls with the same key to execute the function again.
§Deadlocks
The function must not recursively call work or try_work for the same current key because
it would wait for its own result. Work for other keys remains independent.
§Examples
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;
use asyncband::singleflight::Group;
let group = Group::new();
let counter = Arc::new(AtomicUsize::new(0));
let c1 = counter.clone();
let fut1 = group.work("key", || async move {
c1.fetch_add(1, Ordering::SeqCst);
// simulate heavy work to avoid immediate completion
tokio::time::sleep(Duration::from_millis(100)).await;
"result"
});
let c2 = counter.clone();
let fut2 = group.work("key", || async move {
c2.fetch_add(1, Ordering::SeqCst);
// simulate heavy work to avoid immediate completion
tokio::time::sleep(Duration::from_millis(100)).await;
"result"
});
let (r1, r2) = tokio::join!(fut1, fut2);
assert_eq!(r1, "result");
assert_eq!(r2, "result");
assert_eq!(counter.load(Ordering::SeqCst), 1);Sourcepub async fn try_work<E, F>(&self, key: K, func: F) -> Result<V, E>where
F: AsyncFnOnce() -> Result<V, E>,
pub async fn try_work<E, F>(&self, key: K, func: F) -> Result<V, E>where
F: AsyncFnOnce() -> Result<V, E>,
Executes and returns the results of the given function, making sure that only one execution is in-flight for a given key at a time.
If a duplicate comes in, the duplicate caller waits for the original to complete and receives the same results.
If the computation returns an error, it is returned to that caller. After an error, cancellation, or panic, another caller may retry the computation.
Once the function completes successfully, the key, if not forgotten, is removed from
the group, allowing future calls with the same key to execute the function again.
§Deadlocks
The function must not recursively call work or try_work for the same current key because
it would wait for its own result. Work for other keys remains independent.
§Examples
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;
use asyncband::singleflight::Group;
let group = Group::new();
let fut1 = group.try_work("key", || async move {
// simulate heavy work to avoid immediate completion
tokio::time::sleep(Duration::from_millis(100)).await;
Err::<_, &'static str>("fut1")
});
let fut2 = group.try_work("key", || async move {
// simulate heavy work to avoid immediate completion
tokio::time::sleep(Duration::from_millis(200)).await;
Ok::<_, &'static str>("fut2")
});
let (r1, r2) = tokio::join!(fut1, fut2);
assert_eq!(r1, Err("fut1"));
assert_eq!(r2, Ok("fut2"));