Skip to content

Conversation

@tisonkun
Copy link
Collaborator

@tisonkun tisonkun commented Jan 9, 2026

This closes #104.

cc @ariesdevil

Signed-off-by: tison <wander4096@gmail.com>
/// units of work can be executed with duplicate suppression.
#[derive(Debug)]
pub struct Group<K, V> {
map: Mutex<HashMap<K, Arc<OnceCell<V>>>>,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be improved with more efficient concurrent hashmap. But Go's impl use simple Mutext as well so I think it's generally OK.

We can leave using other hashmap impls (and thus extra deps) a follow-up.

Signed-off-by: tison <wander4096@gmail.com>
Comment on lines +119 to +138
let res = cell
.get_or_init(async || {
// I am the leader.
let result = func().await;

// Cleanup: remove the key from the map.
// We must ensure we remove the entry corresponding to *this* cell.
let mut map = self.map.lock();
if let Some(existing) = map.get(&key) {
// Check if the map still points to our cell.
if Arc::ptr_eq(&cell, existing) {
map.remove(&key);
}
}

result
})
.await;

res.clone()
Copy link
Collaborator Author

@tisonkun tisonkun Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative impl method is to build the sync block manually, with potentially registered oneshot channels.

In that case, we can reduce one .clone() if there is no other waiter, and the wakeup logic can be done in batch.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I investigate the OnceCell impl in mea, find some potential problems:

1. leader computed result
2. leader remove key from `Group.map`
3. but OnceCell not yet soon set_value, value_set still false
4. new coming calls see key not in map -> create a new OnceCell -> become new leader

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: This is not a concurrency safety issue, but a semantic safety issue.

Copy link
Collaborator Author

@tisonkun tisonkun Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see ...

I've tried the onshot channels solution, but that can hardly handle if the first leader panics. So I tend to keep the OnceCell solution now at the cost of one extra clone of the result.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I investigate the OnceCell impl in mea, find some potential problems:

1. leader computed result
2. leader remove key from `Group.map`
3. but OnceCell not yet soon set_value, value_set still false
4. new coming calls see key not in map -> create a new OnceCell -> become new leader

This should not be an issue because you can consider the call comes at step 4 "happen after" the result was set and delivered.

Singleflight is not a cache, for sure.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup

Copy link

@ariesdevil ariesdevil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tisonkun
Copy link
Collaborator Author

tisonkun commented Jan 9, 2026

I'm going to merge this patch this week and release 0.6.1.

Even if we do some internal refactor, the public API should remain the same.

@tisonkun tisonkun merged commit e4907f5 into main Jan 9, 2026
9 checks passed
@tisonkun tisonkun deleted the singleflight branch January 9, 2026 18:18
@tisonkun
Copy link
Collaborator Author

tisonkun commented Jan 9, 2026

Merged. I'd stay some time for thinking about it once more.

@ariesdevil feels free to ping me for a release when you need it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add singleflight support

3 participants