singleflight.Group menekan eksekusi function duplikat hanya selama operasi dengan key yang sama masih in-flight. Caller concurrent dapat menerima satu hasil bersama, tetapi caller yang datang setelah completion akan memulai eksekusi baru. Karena itu group adalah mekanisme request coalescing, bukan result cache.
Boundary ini memengaruhi cache fill, metadata refresh, backend read, dan operasi berbasis key lain yang dapat menerima burst pekerjaan concurrent identik. Group dapat mengurangi tekanan simultan pada backing operation tanpa memperpanjang lifetime value yang dikembalikan.
Satu key mengidentifikasi satu active execution
Sebuah Group mempertahankan namespace berisi string key. Do mendaftarkan eksekusi untuk sebuah key lalu menjalankan function yang diberikan. Jika call Do lain dengan key yang sama datang sebelum eksekusi tersebut selesai, duplicate caller menunggu dan menerima value serta error yang sama.
Call dengan key berbeda tidak berbagi hasil. Suppression boundary bersifat key-specific sekaligus temporal: kesamaan key hanya penting selama eksekusi masih aktif.
var g singleflight.Group
v, err, shared := g.Do("account:42", func() (any, error) {
return loadAccount(42)
})Result shared menunjukkan bahwa value yang dikembalikan diberikan kepada lebih dari satu caller. Ia tidak berarti value berasal dari persistent storage di dalam group.
Setelah active call selesai dan entry-nya dihapus, call lain memakai "account:42" akan menjalankan function lagi.
Completion mengakhiri coalescing window
Cache mengaitkan key dengan value selama retention period tertentu. singleflight mengaitkan key dengan active call. Lifetime keduanya secara material berbeda.
Pertimbangkan tiga request yang terpisah oleh completion operasi pertama:
request A ---- execute ---- result
request B ------ wait ----- result
request C ---- execute ---- resultRequest A dan B overlap, sehingga keduanya dapat berbagi satu eksekusi. Request C datang setelah eksekusi pertama selesai, sehingga ia membuat eksekusi baru walaupun key-nya identik.
Perilaku ini membuat singleflight berguna di samping cache, bukan sebagai penggantinya. Jalur cache fill yang umum lebih dulu memeriksa stored state, lalu melakukan coalescing terhadap concurrent miss di sekitar backing read. Cache menentukan retention value; group membatasi duplicate work selama miss window.
Error ikut dibagikan kepada duplicate caller
Shared outcome mencakup value dan error. Jika active function mengembalikan error, duplicate caller yang menunggu key yang sama menerima error tersebut sebagai bagian dari hasil yang sama.
Properti ini dapat sementara waktu menggabungkan burst backend operation yang gagal. Namun ia tidak menciptakan error cache. Setelah failed execution selesai, call berikutnya dapat langsung memulai function lagi.
Perbedaan ini penting saat outage. Coalescing dapat mengurangi concurrent amplification selama slow failing call masih aktif, tetapi tidak memberi jarak retry setelah completion. Backoff, rate limiting, circuit breaking, atau negative caching tetap merupakan policy terpisah.
Do mengikat duplicate caller pada active call
Do bersifat synchronous. Duplicate caller menunggu sampai active function selesai. Package tidak menerima context pada signature Do, sehingga cancellation individual duplicate tidak direpresentasikan langsung oleh method tersebut.
DoChan mengekspos completion melalui receive-only channel. Channel yang dikembalikan menerima satu Result dan tidak ditutup. Caller dapat menggabungkan channel tersebut dengan operasi channel lain, termasuk context cancellation signal, tanpa mengubah lifetime active shared execution.
resultCh := g.DoChan(key, func() (any, error) {
return fetch(key)
})
select {
case result := <-resultCh:
use(result.Val, result.Err)
case <-ctx.Done():
return ctx.Err()
}Keluar dari proses menunggu tidak dengan sendirinya membatalkan function yang terdaftar pada group. Cancellation policy untuk underlying operation harus dirancang terpisah dari duplicate suppression.
Forget mengizinkan overlap untuk key yang sama
Forget menghapus association antara sebuah key dan current in-flight call. Call Do berikutnya untuk key tersebut kemudian dapat memulai function baru alih-alih bergabung dengan eksekusi sebelumnya.
Eksekusi sebelumnya tidak dibatalkan oleh Forget. Ia tetap berjalan secara independen. Karena itu memanggil Forget saat pekerjaan masih aktif dapat menciptakan dua eksekusi simultan untuk logical key yang sama.
Perilaku ini berguna ketika existing call tidak lagi boleh menarik duplicate baru, tetapi mengubah properti inti satu-active-call untuk key tersebut. Kode yang memakai Forget perlu menoleransi operasi overlap dan kemungkinan completion order yang berbeda.
Untuk pekerjaan yang dekat dengan mutation, overlap tersebut dapat signifikan. Duplicate suppression tidak menyediakan serialization, transaction ordering, atau exclusion setelah key di-forget.
Desain key menentukan sharing domain
Group memperlakukan key sebagai opaque string. Application code menentukan operasi mana yang cukup ekuivalen untuk berbagi satu hasil.
Key yang terlalu luas dapat menggabungkan call dengan input yang berbeda pada state yang relevan. Key yang terlalu sempit dapat membuat duplicate work tetap banyak. Parameter yang memengaruhi returned value, authorization scope, tenant identity, representation, atau backend selection mungkin perlu menjadi bagian key.
Group tidak memeriksa function atau membandingkan captured argument. Dua caller yang memakai key sama dapat memberikan function berbeda; jika call keduanya overlap, duplicate akan menerima hasil dari function yang sudah terdaftar untuk key tersebut.
Karena itu key construction adalah bagian dari correctness, bukan sekadar detail optimisasi.
Suppression mengubah bentuk load, bukan lifetime value
Hot key dapat mengubah banyak caller concurrent menjadi satu backend execution ditambah sekumpulan waiter. Ini mengurangi duplicate work selama active interval, tetapi juga mengikat semua caller tersebut pada latency dan outcome dari eksekusi yang dipilih.
Setelah completion, coupling tersebut hilang. Caller berikutnya membuat interval baru dan dapat menjadi eksekusi yang diikuti duplicate berikutnya.
singleflight.Group karena itu memiliki kontrak yang sempit: satu in-flight execution per key pada penggunaan Do normal, shared completion untuk duplicate yang overlap, dan tidak ada retained result setelah eksekusi meninggalkan group. Cache lifetime, retry policy, cancellation ownership, dan durable serialization berada di luar kontrak tersebut.