おそらくはそれさえも平凡な日々

smartcache ~ プリフェッチするインメモリキャッシュ

https://github.com/Songmu/smartcache

smartcacheというGoのインメモリキャッシュライブラリを書いた。

一般的に、キャッシュを実装する場合以下のような問題が起こりがちです。

  • キャッシュ更新時にリクエストが殺到してしまう(いわゆるThundering Herd問題)
  • キャッシュ生成に時間がかかる場合、キャッシュ更新時に処理がブロックしてしまう

smartcacheは上記の問題を以下のアプローチで解決しています。

  • キャッシュ更新処理を一本化する
    • Goの場合 golang.org/x/sync/singleflight を使えば簡単!
  • キャッシュ期限切れ前に内部的に更新処理をおこなう
    • いわゆるプリフェッチ的な処理

使い方

コンストラクタにキャッシュの実際の有効期限、softな有効期限、そしてキャッシュ生成関数を渡します。

// コンストラクタ
ca := smartcache.New(
    10*time.Minute, // 実際のexpire
    time.Minute,    // soft expire
    func(ctx context.Context) (interface{}, error) {
        var val = 1
        return val, nil
    })
// キャッシュ取得
val, err := ca.Get(context.Background())

キャッシュ取得時に、soft expireを過ぎていた場合には既存のキャッシュを返しつつ、内部的に非同期でキャッシュの更新処理をおこないます。

このライブラリの特性上、あまり起きてほしくありませんが、実際の有効期限も切れていた場合には同期的にキャッシュの更新をおこない、その値を返します。これは一般的なキャッシュライブラリ同様の挙動です。soft expireに0を指定した場合は常にこの挙動となりますが、この挙動を利用することは少ないでしょう。

細かい挙動の話をすると、New時点ではキャッシュの生成はおこなわれず、初回のGet時におこなわれます。なので、キャッシュ生成に時間がかかるものを扱う場合、事前に一回Getを叩いておいてもよいでしょう。

このように、smartcacheはほぼフレッシュな値を即時に手に入れたい場合に重宝するはずです。ぜひご利用ください。

余談

こういう処理をちゃんと書くのは地味にめんどいしバグりやすいのでライブラリとしてまとめました。実際最初に書いたやつはテスト書いたらバグっていた…。既存のライブラリがありそうに思うのですが、上手く見つけられなかったので書いてみました。より良いやつがあれば教えて下さい。

このライブラリはKVSのようにキー文字列を指定するような作りにはしていません。不特定な値よりかは、ある程度温め続けて欲しい特定の値を扱うため、失効処理などは一旦必要ないと考えたからです。ただ、なにか良い感じのインターフェースがあればご提案くださると嬉しいです。

汎用キャッシュライブラリなので値が interface{} になってしまうのはちょっとダサい感じはある。この辺りは僕の設計力の問題もありますが、ジェネリクス的なものが欲しくなってしまう部分ではありますね。

ということで、結構特定用途に寄ったライブラリなので、smartcacheと言ってしまうのは名前負け感がちょっとあるのですが、その辺りはあまり良い名前が思いつかなかった…。

created at
last modified at
comments powered by Disqus