edge/pkg/storage/driver/cache/driver.go

207 lines
4.6 KiB
Go

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)
}