func (c *Cache) Put(key string, val interface{}) {
	c.puts.Inc()
	var data []byte
	switch t := val.(type) {
	case nil:
		return
	case *[]byte:
		data = *t
	case []byte:
		data = t
	case *string:
		data = []byte(*t)
	case string:
		data = []byte(t)
	case encoding.BinaryMarshaler:
		var err error
		data, err = t.MarshalBinary()
		if err != nil {
			c.logger.Debugf("could not cache key %s: %v", key, err)
			return
		}
	default:
		panic(errors.Errorf("unimplemented: %T", t))
	}
	if err := c.mem.Set(key, data); err != nil {
		c.logger.Debugf("could not cache key %s in memory: %v", key, err)
	}
	if err := c.store(key, data); err != nil {
		c.logger.Debugf("could not store key %s on disk: %v", key, err)
	}
	if fn := c.cfg.OnAdd; fn != nil {
		fn(key, data)
	}
}
Put stores a value into the Cache. The value must be a []byte,
string, or an object which implements encoding.BinaryMarshaler.
package cache
import (
	"bytes"
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"
	"encoding"
	"encoding/hex"
	"io"
	mathrand "math/rand"
	"os"
	"path/filepath"
	"sync/atomic"
	"time"
	"github.com/Mandala/go-log"
	bigcache "github.com/allegro/bigcache/v3"
	"github.com/google/wire"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/spf13/pflag"
)
var Set = wire.NewSet(
	ProvideCache,
)
type Config struct {
	MaxMem  int     
	MaxDisk int     
	Path    string  
	OnAdd func(key string, data []byte)
	OnMiss func(key string) ([]byte, error)
}
func (c *Config) Bind(flags *pflag.FlagSet) {
	flags.StringVar(&c.Path, "cacheDir", "",
		"persistent cache location")
	flags.IntVar(&c.MaxDisk, "cacheDiskSpace", 1024,
		"the size (in megabytes) of the persistent cache")
	flags.IntVar(&c.MaxMem, "cacheMemory", 256,
		"the size (in megabytes) of the in-memory cache")
}
type Cache struct {
	logger *log.Logger
	cfg    *Config
	mem    *bigcache.BigCache
	diskHits, diskMisses, fnHits, fnMisses, puts prometheus.Counter
	diskUsage                                    prometheus.Gauge
}
func ProvideCache(
	ctx context.Context,
	auto promauto.Factory,
	cfg *Config,
	logger *log.Logger,
) (*Cache, func(), error) {
	cacheCfg := bigcache.DefaultConfig(time.Hour)
	cacheCfg.HardMaxCacheSize = cfg.MaxMem
	cacheCfg.Shards = 128
	cacheCfg.StatsEnabled = true
	cacheCfg.Logger = &logWrapper{logger}
	mem, err := bigcache.NewBigCache(cacheCfg)
	if err != nil {
		return nil, nil, err
	}
	auto.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "cache_mem_entry_count",
		Help: "number of entries in the memory cache",
	}, func() float64 { return float64(mem.Len()) })
	auto.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "cache_mem_hit_count",
		Help: "number of successful memory cache lookups",
	}, func() float64 { return float64(mem.Stats().Hits) })
	auto.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "cache_mem_miss_count",
		Help: "number of successful memory cache lookups",
	}, func() float64 { return float64(mem.Stats().Misses) })
	auto.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "cache_disk_limit_bytes",
		Help: "the ideal disk cache size",
	}, func() float64 { return float64(cfg.MaxDisk * 1024 * 1024) })
	c := &Cache{
		cfg:    cfg,
		logger: logger,
		mem:    mem,
		diskHits: auto.NewCounter(prometheus.CounterOpts{
			Name: "cache_disk_hit_count",
			Help: "number of successful disk cache lookups",
		}),
		diskMisses: auto.NewCounter(prometheus.CounterOpts{
			Name: "cache_disk_miss_count",
			Help: "number of disk cache misses",
		}),
		diskUsage: auto.NewGauge(prometheus.GaugeOpts{
			Name: "cache_disk_used_bytes",
			Help: "the estimated size of the disk cache",
		}),
		fnHits: auto.NewCounter(prometheus.CounterOpts{
			Name: "cache_fn_hit_count",
			Help: "number of third-level cache hits",
		}),
		fnMisses: auto.NewCounter(prometheus.CounterOpts{
			Name: "cache_fn_miss_count",
			Help: "number of third-level cache misses",
		}),
		puts: auto.NewCounter(prometheus.CounterOpts{
			Name: "cache_put_count",
			Help: "number of calls to put()",
		}),
	}
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		defer mem.Close()
		ticker := time.NewTicker(time.Minute)
		defer ticker.Stop()
		var purgeTime int64
		auto.NewCounterFunc(prometheus.CounterOpts{
			Name: "cache_purge_time",
			Help: "the last wall time the a cache purge occurred",
		}, func() float64 { return float64(atomic.LoadInt64(&purgeTime)) })
		for {
			select {
			case <-ctx.Done():
				return
			case <-ticker.C:
				if n, err := c.Prune(); err == nil {
					logger.Tracef("pruned %d files", n)
				} else {
					logger.Warnf("could not prune disk cache: %v", err)
				}
				atomic.StoreInt64(&purgeTime, time.Now().Unix())
			}
		}
	}()
	return c, cancel, nil
}
func (c *Cache) Get(key string, val interface{}) bool {
	var storeMem, storeDisk bool
	data, err := c.mem.Get(key)
	if err == nil {
		goto hit
	}
	storeMem = true
	data, err = c.load(key)
	if err == nil {
		c.diskHits.Inc()
		goto hit
	}
	c.diskMisses.Inc()
	if fn := c.cfg.OnMiss; fn != nil {
		data, err = fn(key)
		storeDisk = true
		if err == nil {
			c.fnHits.Inc()
			goto hit
		}
		c.fnMisses.Inc()
	}
	c.logger.Tracef("cache miss on %s: %v", key, err)
	return false
hit:
	if storeDisk {
		if err := c.store(key, data); err != nil {
			c.logger.Tracef("could not transfer %s to disk: %v", key, err)
		}
	}
	if storeMem {
		if err := c.mem.Set(key, data); err != nil {
			c.logger.Tracef("could not transfer %s to memory (%d bytes): %v", key, len(data), err)
		}
	}
	switch t := val.(type) {
	case nil:
		return false
	case *[]byte:
		*t = data
		return true
	case *string:
		*t = string(data)
		return true
	case encoding.BinaryUnmarshaler:
		if err := t.UnmarshalBinary(data); err != nil {
			c.logger.Debugf("could not unmarshal key %s: %v", key, err)
			return false
		}
		return true
	default:
		panic(errors.Errorf("unimplemented: %T", t))
	}
}