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  }