-
/home/labex/project
ディレクトリにある cacheflight.go
ファイルを開きます。
-
import
に必要な内容を追加します。
import (
"sync"
"time"
"github.com/golang/groupcache/singleflight"
)
-
type Group
と type cacheResult
に必要な内容を追加します。
// cacheResultはキャッシュ結果です
type cacheResult struct {
data interface{} // キャッシュデータ
ctime time.Time // キャッシュ作成日時
isRunning chan bool // 関数が実行中かどうか
}
// Groupはコア構造体です
type Group struct {
cacheExpiration time.Duration // キャッシュの有効期限
sfg *singleflight.Group // singleflightグループ
cache map[string]cacheResult // キャッシュ結果
mu sync.Mutex // ミューテックス
}
-
NewGroup
関数を実装して、指定されたキャッシュ有効期限で新しいキャッシンググループを返します。
func NewGroup(cacheExpiration time.Duration) (group *Group) {
group = &Group{
sfg: &singleflight.Group{},
cache: make(map[string]cacheResult),
cacheExpiration: cacheExpiration,
}
return
}
-
do
関数を実装して、キャッシングロジックを処理します。
func (g *Group) do(key string, fn func() (interface{}, error)) (ret interface{}, err error) {
return g.sfg.Do(key, func() (interface{}, error) {
g.mu.Lock()
result, ok := g.cache[key]
if result.isRunning!= nil {
g.mu.Unlock()
// fnが完了するまで待つ
<-result.isRunning
return g.do(key, fn)
} else if!ok || result.ctime.Add(g.cacheExpiration).Before(time.Now()) {
// キャッシュが存在しないか、期限切れの場合
var run = make(chan bool)
result.isRunning = run
g.cache[key] = result
// fnが完了してrunチャネルをクローズしたとき、他のgoroutineがキャッシュを取得できるようにする
defer close(run)
} else {
// キャッシュが存在し、期限切れでない場合
g.mu.Unlock()
return result.data, nil
}
g.mu.Unlock()
// 異なるキーは同時に実行できる
ret, err = fn()
if err!= nil {
return ret, nil
}
result = cacheResult{
data: ret,
ctime: time.Now(),
}
g.mu.Lock()
g.cache[key] = result
g.mu.Unlock()
return ret, nil
})
}
-
Do
関数を実装して、do
関数のラッパーとします。
func (g *Group) Do(key string, fn func() (interface{}, error)) (ret interface{}, err error) {
return g.do(key, fn)
}