github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/cache/cache.go (about) 1 // Package cache provides an interface-based caching system with multiple implementations. 2 // 3 // The package defines a Cache interface that all implementations must satisfy, 4 // allowing the agent to use different cache backends interchangeably. This design 5 // eliminates the need for nil checks throughout the codebase. 6 // 7 // Available implementations: 8 // - Memcached: Distributed cache using memcached servers 9 // - Noop: No-op cache for testing and fallback (always returns ErrCacheMiss) 10 // 11 // The cache supports multiple memcached servers, custom expiration times, 12 // and automatic serialization/deserialization of Go values using encoding/gob. 13 // 14 // Example usage with memcached: 15 // 16 // cc, err := cache.New(cache.Config{ 17 // Servers: []string{"localhost:11211", "cache1:11211"}, 18 // Logger: logger, 19 // }) 20 // if err != nil { 21 // // Fall back to noop cache 22 // cc = cache.NewNoop(logger) 23 // } 24 // 25 // // Store a struct 26 // err = cc.Set(ctx, "user:123", user, 5*time.Minute) 27 // 28 // // Retrieve a struct 29 // var user User 30 // err = cc.Get(ctx, "user:123", &user) 31 // if err == cache.ErrCacheMiss { 32 // // Key not found, fetch from source 33 // } else if err != nil { 34 // // Other error (network, deserialization, etc.) 35 // log.Warn("cache error: %v", err) 36 // } 37 // 38 // Example usage with noop cache (for testing): 39 // 40 // cc := cache.NewNoop(logger) 41 // // All Get operations return ErrCacheMiss 42 // // All Set/Delete/Flush operations succeed but do nothing 43 package cache 44 45 import ( 46 "context" 47 "flag" 48 "time" 49 50 "github.com/grafana/synthetic-monitoring-agent/internal/error_types" 51 ) 52 53 const ( 54 // ErrCacheMiss is returned when a key is not found in the cache. 55 // This is a sentinel error that allows callers to distinguish between 56 // a cache miss and other errors (network issues, serialization failures, etc.). 57 ErrCacheMiss = error_types.BasicError("cache miss") 58 ) 59 60 // Cache defines the interface for cache operations. 61 // All cache implementations (memcached, noop, local, etc.) must implement this interface. 62 // 63 // This interface allows the agent to use different cache backends interchangeably, 64 // including a no-op implementation that eliminates the need for nil checks in client code. 65 type Cache interface { 66 // Set stores a value in the cache with the specified expiration time. 67 // Returns an error if the operation fails. 68 Set(ctx context.Context, key string, value any, expiration time.Duration) error 69 70 // Get retrieves a value from the cache and deserializes it into dest. 71 // Returns ErrCacheMiss if the key is not found. 72 // Returns an error for other failures (network, serialization, etc.). 73 Get(ctx context.Context, key string, dest any) error 74 75 // Delete removes a key from the cache. 76 // Returns nil if the key doesn't exist (idempotent). 77 Delete(ctx context.Context, key string) error 78 79 // Flush removes all items from the cache. 80 // Use this operation cautiously as it affects all cache users. 81 Flush(ctx context.Context) error 82 } 83 84 type Kind string 85 86 var _ flag.Value = (*Kind)(nil) 87 88 const KindAuto Kind = "auto" 89 90 const ErrUnsupportedCacheKind = error_types.BasicError("unsupported cache type") 91 92 func (val *Kind) Set(s string) error { 93 switch s { 94 case string(KindAuto), string(KindLocal), string(KindMemcached), string(KindNoop): 95 *val = Kind(s) 96 97 return nil 98 99 default: 100 return ErrUnsupportedCacheKind 101 } 102 } 103 104 func (val Kind) String() string { 105 return string(val) 106 }