package cache import ( "context" "strings" "time" ) const ( defaultNamespace = "geo" defaultCacheJitterRatio = 0.1 defaultDeleteScanCount = int64(500) defaultAsyncFillWorkers = 2 defaultAsyncFillBuffer = 1024 defaultAsyncFillTimeout = 2 * time.Second defaultL1TTL = 30 * time.Second ) type Options struct { Namespace string JitterRatio float64 MetricsEnabled bool AsyncFillEnabled bool AsyncFillWorkers int AsyncFillBuffer int AsyncFillTimeout time.Duration L1Enabled bool L1TTL time.Duration DeleteScanCount int64 } func DefaultOptions() Options { return Options{ Namespace: defaultNamespace, JitterRatio: defaultCacheJitterRatio, MetricsEnabled: true, AsyncFillWorkers: defaultAsyncFillWorkers, AsyncFillBuffer: defaultAsyncFillBuffer, AsyncFillTimeout: defaultAsyncFillTimeout, L1TTL: defaultL1TTL, DeleteScanCount: defaultDeleteScanCount, } } type Option func(*Options) func WithNamespace(namespace string) Option { return func(o *Options) { o.Namespace = namespace } } func WithJitterRatio(ratio float64) Option { return func(o *Options) { o.JitterRatio = ratio } } func WithMetrics(enabled bool) Option { return func(o *Options) { o.MetricsEnabled = enabled } } func WithAsyncFill(enabled bool, workers, buffer int, timeout time.Duration) Option { return func(o *Options) { o.AsyncFillEnabled = enabled o.AsyncFillWorkers = workers o.AsyncFillBuffer = buffer o.AsyncFillTimeout = timeout } } func WithL1(enabled bool, ttl time.Duration) Option { return func(o *Options) { o.L1Enabled = enabled o.L1TTL = ttl } } func WithDeleteScanCount(count int64) Option { return func(o *Options) { o.DeleteScanCount = count } } func normalizeOptions(options Options) Options { options.Namespace = strings.TrimSpace(options.Namespace) if options.Namespace == "" { options.Namespace = defaultNamespace } if options.JitterRatio < 0 { options.JitterRatio = 0 } if options.JitterRatio > 1 { options.JitterRatio = 1 } if options.AsyncFillWorkers <= 0 { options.AsyncFillWorkers = defaultAsyncFillWorkers } if options.AsyncFillBuffer <= 0 { options.AsyncFillBuffer = defaultAsyncFillBuffer } if options.AsyncFillTimeout <= 0 { options.AsyncFillTimeout = defaultAsyncFillTimeout } if options.L1TTL <= 0 { options.L1TTL = defaultL1TTL } if options.DeleteScanCount <= 0 { options.DeleteScanCount = defaultDeleteScanCount } return options } type optionsProvider interface { CacheOptions() Options } type optionsCache struct { inner Cache options Options } func newOptionsCache(inner Cache, options Options) Cache { return &optionsCache{inner: inner, options: normalizeOptions(options)} } func (c *optionsCache) CacheOptions() Options { return c.options } func (c *optionsCache) Get(ctx context.Context, key string) ([]byte, error) { return c.inner.Get(ctx, key) } func (c *optionsCache) GetMulti(ctx context.Context, keys []string) (map[string][]byte, error) { return c.inner.GetMulti(ctx, keys) } func (c *optionsCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error { return c.inner.Set(ctx, key, value, ttl) } func (c *optionsCache) SetMulti(ctx context.Context, items []Item) error { return c.inner.SetMulti(ctx, items) } func (c *optionsCache) Delete(ctx context.Context, key string) error { return c.inner.Delete(ctx, key) } func (c *optionsCache) DeleteMany(ctx context.Context, keys []string) error { return c.inner.DeleteMany(ctx, keys) } func (c *optionsCache) DeletePrefix(ctx context.Context, prefix string) error { return c.inner.DeletePrefix(ctx, prefix) }