よくあるRedisやmemcacheでキャッシュするパターンをサポートするライブラリのGoクライアントを作りました。
こんなやつです。
アクセスしたい情報や変数からキー名を生成する
if (Redisにデータがある) {
RedisからGetする
} else {
DatabaseからGetしたり、APIからfetchする
RedisへSetする
}
Redisで使う想定ですが、Clientを実装すればどのようなキャッシュにも使えます。
functionを渡せばそれがキャッシュされるので、DBでもAPIアクセスでも使えます。
データをserializeして set, getすることもできます。
詳しい使い方は、READMEとテストコードを見てください。
go-cache-fetcher-client
Golangのキャッシュフェッチャーです。
Installation
go get github.com/peutes/cachefetcher
✅ キャッシュフェッチャークライアントの基本ロジックをサポート
様々なファンクションのレスポンスをRedis, Memcached、その他のキャッシュシステムにキャッシュできます。
たとえば、最初にファンクションからレスポンスを取得する際にRedisに保存し、ファンクションのレスポンスを取得し、
次にもしキャッシュがあったらRedisから取得できます。
✅ 要素からキャッシュキーを自動生成
キャッシュキーの生成ロジックについて考える必要はありません。
キー生成ロジックはメインロジックから隠蔽できます。
✅ シンプルなキャッシュコントロール
キャッシュコントロールはとてもシンプルで、キーをセットし、フェッチャー関数をフェッチするだけです。
このフェッチャークライアントは使用するためには、 SetKey と Fetch 関数のみで十分に使用できます。
Fetch には、フェッチャー関数、取得用値のポインタ、キャッシュ有効期限をセットします。
SetKey()Fetch()
インストール
go get github.com/peutes/go-cache-fetcher-client
使い方
fetcher := cachefetcher.NewCacheFetcher(
&ClientImpl{
Rdb: redis.NewClient(&redis.Options{Addr: "localhost:6379"}),
},
nil
)
fetcher.SetKey([]string{"prefix", "str"}, "hoge")
// fetcher.Key() == "prefix_str_hoge"
// フェッチャー関数。たとえば、DBから読み込みを想定します。
read := func(s string) string {
return s + " fetch!!"
}
// 最初のフェッチはフェッチャー関数から呼ばれます。キャッシュは呼ばれません。
var dst string
err := fetcher.Fetch(10*time.Second, &dst, func() (string, error) {
return read("first"), nil
})
// dst == "first fetch!!" <- get from function
// 2回目のフェチは有効期限内のキャッシュから呼ばれます。フェッチャー関数からは呼ばれません。
err = fetcher.Fetch(10*time.Second, &dst, func() (string, error) {
return read("second"), nil
})
// dst == "first fetch!!" <- get from cache
サポートされてる型
キー要素は、stringの他、 int, float, bool, complex, byte, time, slice, array, String() メソッドがある struct に対応しています。
このクライアントは gob serializer によるシリアライズをサポートしています。
キャッシュにはシリアライズされた文字列が保存されます。
fetcher.SetKey([]string{"prefix", "any"}, 1, 0.1, true, &[]string{"a", "b"}, time.Unix(0, 0).In(time.UTC))
_ = fetcher.Key() // "prefix_any_1_0.1_true_a_b_1970-01-01_00:00:00_+0000_UTC"
_ = fetcher.HashKey() // "prefix_any_c94a415eb6e20585f4fbc856b6edcf52007259522967c4bea548515e71531663"
read := func() ([]int, error) {
return []int{1, 2, 3, 4, 5}
}
var dst []int
err := fetcher.Fetch(10*time.Second, &dst, read)
// dst == []int{1, 2, 3, 4, 5}
要素は、文字列タイプ以外もサポートしています。
もし interface{} や独自のタイプを使用したい場合は、 GobRegister() を使って登録します。
i := 10
b := true
s := "abc"
ft := 0.123
i8 := int8(20)
i64 := int64(30)
ui8 := uint8(40)
ui64 := uint64(50)
e := &testStruct{
I: i,
B: b,
S: s,
F: ft,
I8: i8,
I64: i64,
UI8: ui8,
UI64: ui64,
IP: &i,
BP: &b,
SP: &s,
FP: &ft,
I8P: &i8,
I64P: &i64,
UI8P: &ui8,
UI64P: &ui64,
IS: []int{i, i, i},
BS: []bool{b, b, b},
SS: []string{s, s, s},
FS: []float64{ft, ft, ft},
IM: map[int]int{1: i, 2: i, 3: i},
BM: map[bool]bool{true: b, false: b},
SM: map[string]string{"a": s, "bb": s, "ccc": s},
FM: map[float64]float64{0.1: ft, 0.2: ft, 0.3: ft},
}
var dst testStruct
f := cachefetcher.NewCacheFetcher(redisClient, options)
err := fetcher.SetKey([]string{"prefix", "key"}, "struct1")
err = fetcher.Set(e, 10*time.Second)
err = fetcher.Get(&dst)
他のキャッシュコントロール
もし、ハッシュキーが必要であれば、 SetKey のかわりに、 SetHashKey を使います。
個々に、 Set(), Get(), Del() も使えます。
もし、キーが欲しい場合は、Key()を使います。
もし、キャッシュされているか?の結果をboolで欲しい場合は、IsCached()を使います。
SetHashKey()Set()Get()SetString()GetString()Del()Key()IsCached()GobRegister()
キャッシュクライアントの実装
このキャッシュフェッチャークライアントは、キャッシュクライアントの実装が求められますが、
独自実装する代わりに必要最低限のシンプルなRedisクライアントを用意しているので、このクライアントを使えば動かすことができます。
// SimpleRedisClientImpl はサンプルクライアントの実装です。
type SimpleRedisClientImpl struct {
Rdb *redis.Client
}
// サンプルクライアントの `Set` 実装です。
func (i *SimpleRedisClientImpl) Set(key string, value interface{}, expiration time.Duration) error {
// You need an implementation to set from the cache.
return i.Rdb.Set(ctx, key, value, expiration).Err()
}
// サンプルクライアントの `Get` 実装です。
func (i *SimpleRedisClientImpl) Get(key string, dst interface{}) error {
// You need an implementation to get from the cache.
v, err := i.Rdb.Get(ctx, key).Result()
if err != nil {
return err
}
reflect.ValueOf(dst).Elem().SetString(v)
return nil
}
// サンプルクライアントの `Del` 実装です。
func (i *SimpleRedisClientImpl) Del(key string) error {
return i.Rdb.Del(ctx, key).Err()
}
// サンプルクライアントの `IsErrCacheMiss` 実装です。
// キャッシュミスエラーの判定を返してください。
func (i *SimpleRedisClientImpl) IsErrCacheMiss(err error) bool {
return errors.Is(err, redis.Nil)
}
オプション
このフェッチャークライアントは single flight を設定で使えます。
もし、 DebugPrintMode を有効にすると、ターミナルにキーが出力されます。
cachefetcher.Options{
Group: &singleflight.Group{}, // default
GroupTimeout: 30 * time.Second, // default
DebugPrintMode: true, // default is false
})