2023-10-24 22:52:33 +02:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
2023-11-29 11:10:29 +01:00
|
|
|
"context"
|
|
|
|
"fmt"
|
2023-10-24 22:52:33 +02:00
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"forge.cadoles.com/arcad/edge/pkg/storage"
|
|
|
|
"forge.cadoles.com/arcad/edge/pkg/storage/driver"
|
2023-11-29 11:10:29 +01:00
|
|
|
"github.com/allegro/bigcache/v3"
|
2023-10-24 22:52:33 +02:00
|
|
|
"github.com/pkg/errors"
|
2023-11-29 11:10:29 +01:00
|
|
|
"gitlab.com/wpetit/goweb/logger"
|
2023-10-24 22:52:33 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
driver.RegisterBlobStoreFactory("cache", blobStoreFactory)
|
|
|
|
}
|
|
|
|
|
|
|
|
func blobStoreFactory(dsn *url.URL) (storage.BlobStore, error) {
|
|
|
|
query := dsn.Query()
|
|
|
|
|
2023-11-29 11:10:29 +01:00
|
|
|
rawDriver := query.Get("driver")
|
|
|
|
if rawDriver == "" {
|
|
|
|
return nil, errors.New("missing required url parameter 'driver'")
|
2023-10-24 22:52:33 +02:00
|
|
|
}
|
|
|
|
|
2023-11-29 11:10:29 +01:00
|
|
|
query.Del("driver")
|
2023-10-24 22:52:33 +02:00
|
|
|
|
2023-11-30 19:09:51 +01:00
|
|
|
blobStoreOptionFuncs := make([]OptionFunc, 0)
|
2023-10-24 22:52:33 +02:00
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
cacheTTL, err := parseDuration(&query, "cacheTTL")
|
|
|
|
if err != nil {
|
|
|
|
if !errors.Is(err, errNotFound) {
|
|
|
|
return nil, errors.WithStack(err)
|
2023-11-29 11:10:29 +01:00
|
|
|
}
|
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
cacheTTL = time.Hour
|
2023-10-24 22:52:33 +02:00
|
|
|
}
|
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithCacheTTL(cacheTTL))
|
2023-11-29 11:10:29 +01:00
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
cacheConfig, err := parseBigCacheConfig(&query, cacheTTL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not parse big cache config")
|
2023-11-30 19:09:51 +01:00
|
|
|
}
|
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithBigCacheConfig(*cacheConfig))
|
2023-11-30 19:09:51 +01:00
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
blobBucketCacheSize, err := parseInt(&query, "blobBucketCacheSize")
|
|
|
|
if err != nil {
|
|
|
|
if !errors.Is(err, errNotFound) {
|
|
|
|
return nil, errors.WithStack(err)
|
2023-11-29 11:10:29 +01:00
|
|
|
}
|
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
blobBucketCacheSize = 16
|
2023-10-24 22:52:33 +02:00
|
|
|
}
|
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithBucketCacheSize(int(blobBucketCacheSize)))
|
2023-11-29 11:10:29 +01:00
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
bloInfoCacheSize, err := parseInt(&query, "bloInfoCacheSize")
|
|
|
|
if err != nil {
|
|
|
|
if !errors.Is(err, errNotFound) {
|
|
|
|
return nil, errors.WithStack(err)
|
2023-11-29 11:10:29 +01:00
|
|
|
}
|
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
bloInfoCacheSize = 16
|
2023-11-30 19:09:51 +01:00
|
|
|
}
|
2023-11-29 11:10:29 +01:00
|
|
|
|
2023-12-03 14:26:18 +01:00
|
|
|
blobStoreOptionFuncs = append(blobStoreOptionFuncs, WithBlobInfoCacheSize(int(bloInfoCacheSize)))
|
2023-10-24 22:52:33 +02:00
|
|
|
|
|
|
|
url := &url.URL{
|
|
|
|
Scheme: rawDriver,
|
|
|
|
Host: dsn.Host,
|
|
|
|
Path: dsn.Path,
|
|
|
|
RawQuery: query.Encode(),
|
|
|
|
}
|
|
|
|
|
2023-11-30 19:09:51 +01:00
|
|
|
backend, err := driver.NewBlobStore(url.String())
|
2023-10-24 22:52:33 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
2023-11-30 19:09:51 +01:00
|
|
|
store, err := NewBlobStore(backend, blobStoreOptionFuncs...)
|
2023-11-29 11:10:29 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
2023-11-30 19:09:51 +01:00
|
|
|
return store, nil
|
2023-10-24 22:52:33 +02:00
|
|
|
}
|
2023-11-29 11:10:29 +01:00
|
|
|
|
|
|
|
type cacheLogger struct{}
|
|
|
|
|
|
|
|
func (l *cacheLogger) Printf(format string, v ...interface{}) {
|
|
|
|
logger.Debug(context.Background(), fmt.Sprintf(format, v...))
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ bigcache.Logger = &cacheLogger{}
|
2023-12-03 14:26:18 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|