mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-09-30 12:01:59 +00:00
Make cache-write failures non-fatal in GetOrSet/Remember, replace the unsynchronised default cache with an atomic pointer, fix tag-index leaks and write-lock-on-read in the memory provider, add a janitor, closed state and byte copies, hash/namespace memcache keys with CAS tag index, gate Clear behind AllowFlush, return ErrNotFound without the key, and allowlist Redis stats. Mark audit status.
90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var defaultCache atomic.Pointer[Cache]
|
|
|
|
// swapOwned installs c as the default and closes the displaced cache, which this
|
|
// package created and therefore owns.
|
|
func swapOwned(c *Cache) {
|
|
if old := defaultCache.Swap(c); old != nil && old != c {
|
|
_ = old.Close() // best-effort: the displaced provider is being discarded
|
|
}
|
|
}
|
|
|
|
// Initialize initializes the cache with a provider.
|
|
// If not called, the package will use an in-memory provider by default.
|
|
func Initialize(provider Provider) {
|
|
swapOwned(NewCache(provider))
|
|
}
|
|
|
|
// UseMemory configures the cache to use in-memory storage.
|
|
func UseMemory(opts *Options) error {
|
|
swapOwned(NewCache(NewMemoryProvider(opts)))
|
|
return nil
|
|
}
|
|
|
|
// UseRedis configures the cache to use Redis storage.
|
|
func UseRedis(config *RedisConfig) error {
|
|
provider, err := NewRedisProvider(config)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to initialize Redis provider: %w", err)
|
|
}
|
|
swapOwned(NewCache(provider))
|
|
return nil
|
|
}
|
|
|
|
// UseMemcache configures the cache to use Memcache storage.
|
|
func UseMemcache(config *MemcacheConfig) error {
|
|
provider, err := NewMemcacheProvider(config)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to initialize Memcache provider: %w", err)
|
|
}
|
|
swapOwned(NewCache(provider))
|
|
return nil
|
|
}
|
|
|
|
// GetDefaultCache returns the default cache instance.
|
|
// Initializes with in-memory provider if not already initialized.
|
|
// Safe for concurrent use.
|
|
func GetDefaultCache() *Cache {
|
|
if c := defaultCache.Load(); c != nil {
|
|
return c
|
|
}
|
|
fresh := NewCache(NewMemoryProvider(&Options{
|
|
DefaultTTL: 5 * time.Minute,
|
|
MaxSize: 10000,
|
|
}))
|
|
if defaultCache.CompareAndSwap(nil, fresh) {
|
|
return fresh
|
|
}
|
|
_ = fresh.Close() // lost the race; discard our provider
|
|
return defaultCache.Load()
|
|
}
|
|
|
|
// SetDefaultCache sets a custom cache instance as the default cache.
|
|
// This is useful for testing or when you want to use a pre-configured cache instance.
|
|
// The caller keeps ownership of both the new and the displaced cache; neither is closed.
|
|
func SetDefaultCache(cache *Cache) {
|
|
defaultCache.Store(cache)
|
|
}
|
|
|
|
// GetStats returns cache statistics.
|
|
func GetStats(ctx context.Context) (*CacheStats, error) {
|
|
cache := GetDefaultCache()
|
|
return cache.Stats(ctx)
|
|
}
|
|
|
|
// Close closes the cache and releases resources.
|
|
func Close() error {
|
|
if c := defaultCache.Load(); c != nil {
|
|
return c.Close()
|
|
}
|
|
return nil
|
|
}
|