【Go】bigcacheの操作をラップしたインターフェースを作ってみた


## 概要

- Goのin-memory cache packageの`bigcache`パッケージを使用したサンプルコードと使用例
	- Get, Setなどの操作をインターフェース化
- encode / decodeは`gob`パッケージを使用

## サンプルコード


```go
package main

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"

	"github.com/allegro/bigcache/v3"
)

// Cache bigcacheの操作を抽象化したインターフェース
type Cache interface {
	Get(key string, mapper interface{}) error
	Set(key string, value interface{}) error
	Delete(key string) error
	Reset() error
}

type BigCache struct {
	Cache *bigcache.BigCache
}

// NewCache bigcacheのインスタンスを生成する関数
func NewCache(c *bigcache.BigCache) (Cache, error) {
	if c == nil {
		return nil, errors.New("cache instance is nil")
	}
	return &BigCache{
		Cache: c,
	}, nil
}

// 登録済みの型を保持するマップ
var registeredTypes = make(map[string]bool)

// registerType 型を一度だけ登録するための関数
func registerType(value interface{}) {
	if value == nil {
		return
	}
	typeName := fmt.Sprintf("%T", value)
	if !registeredTypes[typeName] {
		gob.Register(value)
		registeredTypes[typeName] = true
	}
}

// Get キャッシュからデータを取得する関数
func (c BigCache) Get(key string, mapper interface{}) error {
	if key == "" {
		return errors.New("key cannot be empty")
	}
	if mapper == nil {
		return errors.New("mapper cannot be nil")
	}

	registerType(mapper)

	entry, err := c.Cache.Get(key)
	if err != nil {
		return err
	}
	return gob.NewDecoder(bytes.NewReader(entry)).Decode(mapper)
}

func (c BigCache) Set(key string, value interface{}) error {
	if key == "" {
		return errors.New("key cannot be empty")
	}
	vb, err := serialize(value)
	if err != nil {
		return err
	}
	return c.Cache.Set(key, vb)
}

// serialize キャッシュに保存するデータをシリアライズする関数
func serialize(value interface{}) ([]byte, error) {
	registerType(value)

	buf := bytes.Buffer{}
	enc := gob.NewEncoder(&buf)

	err := enc.Encode(value)
	if err != nil {
		return nil, fmt.Errorf("failed to encode: %w", err)
	}

	return buf.Bytes(), nil
}

// Delete キャッシュからデータを削除する関数
func (c BigCache) Delete(key string) error {
	if key == "" {
		return errors.New("key cannot be empty")
	}
	return c.Cache.Delete(key)
}

// Reset キャッシュをリセットする関数
func (c BigCache) Reset() error {
	return c.Cache.Reset()
}
```


## 使用例


```go
package main

import (
	"context"
	"errors"
	"log"
	"math/big"
	"time"

	"github.com/allegro/bigcache/v3"
)

func main() {
	// bigcacheの設定
	cfg := bigcache.DefaultConfig(10 * time.Minute)
	bigCa, err := bigcache.New(context.Background(), cfg)
	if err != nil {
		log.Fatal(err)
	}

	// bigcacheの操作を抽象化したインスタンスを生成
	cache, err := NewCache(bigCa)
	if err != nil {
		log.Fatal(err)
	}

	// bigint型のデータをキャッシュに保存
	var bigIntValue big.Int
	bigIntValue.SetString("1234567890", 10)

	cache.Set("test-key", &bigIntValue)
	if err != nil {
		log.Fatal(err)
	}

	// キャッシュからデータを取得
	var result big.Int
	err = cache.Get("test-key", &result)
	if err != nil {
		log.Fatal(err)
	}
	println("result:", result.String())

	// キャッシュからデータを削除
	err = cache.Delete("test-key")
	if err != nil {
		log.Fatal(err)
	}

	// Getでキャッシュが空かどうかを確認
	var result2 big.Int
	err = cache.Get("test-key", &result2)
	if err != nil {
		if errors.Is(err, bigcache.ErrEntryNotFound) {
			println(err.Error())
		} else {
			println(err.Error())
		}
	}
	println("result2:", result2.String())
}
// 出力
// result: 1234567890
// Entry not found
// result2: 0
```