package cache import ( "context" "fmt" "net/url" "strconv" "time" "forge.cadoles.com/arcad/edge/pkg/storage" "forge.cadoles.com/arcad/edge/pkg/storage/driver" "github.com/allegro/bigcache/v3" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) func init() { driver.RegisterBlobStoreFactory("cache", blobStoreFactory) } func blobStoreFactory(dsn *url.URL) (storage.BlobStore, error) { query := dsn.Query() rawDriver := query.Get("driver") if rawDriver == "" { return nil, errors.New("missing required url parameter 'driver'") } query.Del("driver") blobStoreOptionFuncs := make([]OptionFunc, 0) cacheTTL, err := parseDuration(&query, "cacheTTL") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } cacheTTL = time.Hour } blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithCacheTTL(cacheTTL)) cacheConfig, err := parseBigCacheConfig(&query, cacheTTL) if err != nil { return nil, errors.Wrap(err, "could not parse big cache config") } blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithBigCacheConfig(*cacheConfig)) blobBucketCacheSize, err := parseInt(&query, "blobBucketCacheSize") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } blobBucketCacheSize = 16 } blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithBucketCacheSize(int(blobBucketCacheSize))) bloInfoCacheSize, err := parseInt(&query, "bloInfoCacheSize") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } bloInfoCacheSize = 16 } blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithBlobInfoCacheSize(int(bloInfoCacheSize))) url := &url.URL{ Scheme: rawDriver, Host: dsn.Host, Path: dsn.Path, RawQuery: query.Encode(), } backend, err := driver.NewBlobStore(url.String()) if err != nil { return nil, errors.WithStack(err) } store, err := NewBlobStore(backend, blobStoreOptionFuncs...) if err != nil { return nil, errors.WithStack(err) } return store, nil } type cacheLogger struct{} func (l *cacheLogger) Printf(format string, v ...interface{}) { logger.Debug(context.Background(), fmt.Sprintf(format, v...)) } var _ bigcache.Logger = &cacheLogger{} func parseBigCacheConfig(query *url.Values, cacheTTL time.Duration) (*bigcache.Config, error) { config := bigcache.DefaultConfig(cacheTTL) config.Logger = &cacheLogger{} hardMaxCacheSize, err := parseInt(query, "bigCacheHardMaxCacheSize") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } hardMaxCacheSize = int64(config.HardMaxCacheSize) } config.HardMaxCacheSize = int(hardMaxCacheSize) maxEntriesInWindow, err := parseInt(query, "bigCacheMaxEntriesInWindow") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } maxEntriesInWindow = int64(config.MaxEntriesInWindow) } config.MaxEntriesInWindow = int(maxEntriesInWindow) shards, err := parseInt(query, "bigCacheShards") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } shards = int64(config.Shards) } config.Shards = int(shards) maxEntrySize, err := parseInt(query, "bigCacheMaxEntrySize") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } maxEntrySize = int64(config.MaxEntrySize) } config.MaxEntrySize = int(maxEntrySize) cleanWindow, err := parseDuration(query, "bigCacheCleanWindow") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } cleanWindow = config.CleanWindow } config.CleanWindow = cleanWindow lifeWindow, err := parseDuration(query, "bigCacheLifeWindow") if err != nil { if !errors.Is(err, errNotFound) { return nil, errors.WithStack(err) } lifeWindow = config.LifeWindow } config.LifeWindow = lifeWindow return &config, nil } var errNotFound = errors.New("not found") func parseInt(query *url.Values, name string) (int64, error) { rawValue := query.Get(name) if rawValue != "" { query.Del(name) value, err := strconv.ParseInt(rawValue, 10, 32) if err != nil { return 0, errors.Wrapf(err, "could not parse url parameter '%s'", name) } return value, nil } return 0, errors.WithStack(errNotFound) } func parseDuration(query *url.Values, name string) (time.Duration, error) { rawValue := query.Get(name) if rawValue != "" { query.Del(name) value, err := time.ParseDuration(rawValue) if err != nil { return 0, errors.Wrapf(err, "could not parse url parameter '%s'", name) } return value, nil } return 0, errors.WithStack(errNotFound) }