Skip to content

Commit 1ac840d

Browse files
authored
Merge pull request #806 from devlights/add-singleflight-example
2 parents 2516432 + 5c393cb commit 1ac840d

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
app
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# これは何?
2+
3+
```golang.org/x/sync/singleflight``` のサンプルです。
4+
5+
singleflightパッケージは、重複した関数呼び出しを抑制するためのメカニズムを提供します。
6+
7+
このパッケージは、特に高価な操作や重複する操作が同時に複数のゴルーチンから要求される場合に有効です。
8+
9+
singleflightパッケージは、golang.org/x/sync/singleflightライブラリに含まれており、主に以下の機能を提供します。
10+
11+
- 重複呼び出しの抑制:同じキーに対する複数のリクエストが同時に発生した場合、最初のリクエストが完了するまで他のリクエストを待機させ、結果を共有します。
12+
- 効率の向上:重複した操作を防ぐことで、サービスやデータベースへの不要な負荷を軽減します。
13+
- シンプルなAPI:Group型を使用して、重複する操作を管理します。
14+
15+
Cache Stampedeなどが発生する可能性がある部分などで利用出来ます。
16+
17+
```Group.Forget()``` が存在するのがちょっとした違い。
18+
19+
## 参考情報
20+
21+
- https://pkg.go.dev/golang.org/x/sync/singleflight
22+
- https://twitter.com/func25/status/1778770235316916427?t=39VDEN8c8WFp9fc-JGBABA&s=19
23+
- https://zenn.dev/nkmrkz/articles/go-singleflight
24+
- https://christina04.hatenablog.com/entry/go-singleflight
25+
- https://pkg.go.dev/sync@go1.22.3#OnceValue
26+
- https://cs.opensource.google/go/x/sync/+/refs/tags/v0.7.0:singleflight/singleflight.go
27+
- https://blog.wu-boy.com/2024/02/how-to-reslove-the-hotspot-invalid-using-singleflight-en/
28+
- https://www.codingexplorations.com/blog/understanding-singleflight-in-golang-a-solution-for-eliminating-redundant-work
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# https://taskfile.dev
2+
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- task: build
9+
- task: run
10+
build:
11+
cmds:
12+
- go build -o app{{.exeEXT}}
13+
run:
14+
cmds:
15+
- ./app{{.exeEXT}}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"sync"
6+
"time"
7+
8+
"golang.org/x/sync/singleflight"
9+
)
10+
11+
func init() {
12+
log.SetFlags(0)
13+
}
14+
15+
func heavy(delay time.Duration, prefix string) {
16+
log.Printf("%s start", prefix)
17+
defer log.Printf("%s end ", prefix)
18+
19+
time.Sleep(delay)
20+
}
21+
22+
func main() {
23+
// Group.Do() or Group.DoChan() を利用して
24+
// 重複する呼び出しが発生する箇所や処理負荷が高い操作などの呼び出しを
25+
// 抑制することが出来る。
26+
//
27+
// Cache Stampedeが発生する可能性がある部分には非常に有効です。
28+
29+
const (
30+
KEY = "FUNC-GROUP-KEY"
31+
)
32+
33+
var (
34+
grp = &singleflight.Group{}
35+
ready = make(chan struct{})
36+
results = make(chan (<-chan singleflight.Result))
37+
wg = sync.WaitGroup{}
38+
)
39+
40+
wg.Add(3)
41+
go func() {
42+
defer wg.Done()
43+
44+
results <- grp.DoChan(KEY, func() (any, error) {
45+
<-ready
46+
heavy(3*time.Second, "func1")
47+
return "func1", nil
48+
})
49+
}()
50+
go func() {
51+
defer wg.Done()
52+
53+
results <- grp.DoChan(KEY, func() (any, error) {
54+
<-ready
55+
heavy(1*time.Second, "func2")
56+
return "func2", nil
57+
})
58+
}()
59+
go func() {
60+
defer wg.Done()
61+
62+
results <- grp.DoChan(KEY, func() (any, error) {
63+
<-ready
64+
heavy(2*time.Second, "func3")
65+
return "func3", nil
66+
})
67+
}()
68+
go func() {
69+
defer close(results)
70+
wg.Wait()
71+
}()
72+
73+
// よーいドン
74+
close(ready)
75+
76+
for ret := range results {
77+
log.Printf("%+v", <-ret)
78+
}
79+
80+
// 更に追加呼び出し
81+
// ただし、実行する前に Group.Forget() を呼び出して
82+
// キーに紐づく結果を忘れさせてから実行
83+
grp.Forget(KEY)
84+
ret := grp.DoChan(KEY, func() (any, error) {
85+
heavy(1*time.Second, "func4")
86+
return "func4", nil
87+
})
88+
log.Printf("%+v", <-ret)
89+
90+
/*
91+
task: [build] go build -o app
92+
task: [run] ./app
93+
func2 start
94+
func2 end
95+
{Val:func2 Err:<nil> Shared:true}
96+
{Val:func2 Err:<nil> Shared:true}
97+
{Val:func2 Err:<nil> Shared:true}
98+
func4 start
99+
func4 end
100+
{Val:func4 Err:<nil> Shared:false}
101+
*/
102+
}

0 commit comments

Comments
 (0)