Documentation
¶
Overview ¶
Package inmem provides a thread-safe, configurable in-memory implementation of the cache.Cache and cache.TTLCache interfaces.
Overview ¶
This package offers `SafeMapCache`, an in-memory cache using a Go map protected by a single `sync.RWMutex`. It supports item expiration (TTL), configurable capacity limits, and automatic removal of expired items via a background garbage collector.
Note: Due to the single mutex design, this implementation is best suited for low-to-moderate concurrency scenarios. High contention on the lock might become a bottleneck.
Features ¶
- Thread-Safe: Uses `sync.RWMutex` for safe concurrent operations.
- Configurable: Accepts options via `NewSafeMap` for:
- Default TTL (`WithDefaultTTL`, default: 0/no expiry).
- Maximum number of items (`WithMaxSize`, default: 10000).
- Garbage collection interval (`WithGCInterval`, default: 5 minutes, min: 5 seconds).
- TTL Support: Items expire after their TTL (set via `SetWithTTL` or the configured default).
- Size Limiting: Returns `cache.ErrCacheFull` if `Set` attempts to add a new item when the cache is at `MaxSize`.
- Garbage Collection: A background goroutine periodically removes expired items based on the configured interval.
- Efficient `Len()`: Provides an O(1) approximate count of active items using an atomic counter.
- Explicit Cleanup: Requires calling the `Stop()` method to gracefully shut down the garbage collector and clear the cache.
- Context Aware: Operations respect context cancellation/deadlines.
- Testable Time: Uses a `Clock` interface for reliable time-based testing.
Usage ¶
Create a new cache using `NewSafeMap`. It is **essential** to call the `Stop()` method when the cache is no longer needed to ensure the background garbage collector goroutine is terminated cleanly. Using `defer` is a common pattern.
// Cache with a default 1-hour TTL, max 5000 items, and 10-minute GC interval
cacheOpts := []inmem.SafeMapCacheOption[string, []byte]{
inmem.WithDefaultTTL[string, []byte](1*time.Hour),
inmem.WithMaxSize[string, []byte](5000),
inmem.WithGCInterval[string, []byte](10*time.Minute),
}
c := inmem.NewSafeMap(cacheOpts...)
defer c.Stop(context.Background()) // Ensure Stop is called for cleanup
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = c.Set(ctx, "config:data", someBytes) // Uses default TTL (1 hour)
_ = c.SetWithTTL(ctx, "session:abc", tokenBytes, 15*time.Minute) // Explicit TTL
val, err := c.Get(ctx, "config:data")
// ... handle error or use val ...
count := c.Len() // Get approximate count
fmt.Printf("Approximate items in cache: %d\n", count)
_ = c.Delete(ctx, "session:abc") // Mark session for deletion
Important Considerations ¶
- `Delete` Behavior: Calling `Delete(ctx, key)` marks the item as expired immediately (subsequent `Get(ctx, key)` will return `cache.ErrExpired`) and decrements the counter used by `Len()`. However, the key and value **remain in memory** until the next garbage collection cycle physically removes them from the underlying map. Memory is not reclaimed instantly upon calling `Delete`.
- `Len()` Approximation: `Len()` returns the count of items considered active (added minus deleted/GC'd). It may include items that have passed their TTL but have not yet been removed by the garbage collector.
- Memory Usage: Size is limited by available RAM and the `MaxSize` setting.
- Resource Cleanup: **Failure to call the `Stop()` method will result in leaking the garbage collector goroutine.**
Index ¶
- func NewGarbageCollector[K comparable, V any](interval time.Duration) *garbageCollector[K, V]
- func NewSafeMap[K comparable, V any](opts ...SafeMapCacheOption[K, V]) *safeMapCache[K, V]
- type Clock
- type GarbageCollector
- type SafeMapCache
- func (c SafeMapCache) Clear(ctx context.Context) error
- func (c SafeMapCache) Delete(ctx context.Context, key K) error
- func (c SafeMapCache) Get(ctx context.Context, key K) (V, error)
- func (c SafeMapCache) Len() int
- func (c SafeMapCache) Set(ctx context.Context, key K, value V) error
- func (c SafeMapCache) SetWithTTL(ctx context.Context, key K, value V, ttl time.Duration) error
- func (c SafeMapCache) Stop(ctx context.Context) error
- type SafeMapCacheOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewGarbageCollector ¶
func NewGarbageCollector[K comparable, V any](interval time.Duration) *garbageCollector[K, V]
NewGarbageCollector creates a new garbage collector with the specified interval. The interval must be greater than 0. If not, nil is returned.
func NewSafeMap ¶
func NewSafeMap[K comparable, V any](opts ...SafeMapCacheOption[K, V]) *safeMapCache[K, V]
NewSafeMap creates a new instance of safeMapCache. It initializes the items map and sets up the garbage collector. The garbage collector is started in a separate goroutine. The clock is set to the real time package by default.
Types ¶
type Clock ¶
type Clock interface {
// Now returns the current time.
Now() time.Time
// NewTicker creates a new Ticker that will send ticks on the
// returned channel. The ticks will be sent at intervals of d.
NewTicker(d time.Duration) *time.Ticker
}
Clock is an interface that provides methods to get the current time, wait for a duration, and create a new ticker. It allows for easy mocking and testing of time-dependent code. The default implementation uses the real time package.
type GarbageCollector ¶
type GarbageCollector[K comparable, V any] interface { // Start begins the garbage collection process. // This should be called in a separate goroutine. // E.g. go gc.Start(c) Start(c garbageCollection[K, V]) // Stop stops the garbage collection process. Stop(ctx context.Context) error // IsActive returns true if the garbage collector is currently running. IsActive() bool }
GarbageCollector is an interface that defines the methods for a garbage collector. It is responsible for periodically deleting expired items from the cache.
type SafeMapCache ¶
type SafeMapCache[K comparable, V any] struct { // contains filtered or unexported fields }
func (SafeMapCache) Clear ¶
Clear removes all items from the cache. It uses a write lock to ensure thread safety. It checks if the context is canceled or timed out. If the context is canceled or times out, it returns an error.
func (SafeMapCache) Delete ¶
Delete sets an item from the cache to expire. It does not actually delete the item from the cache. The actual deletion is handled by the garbage collector. If the item does not exist, it does nothing. It uses a write lock to ensure thread safety. It checks if the context is canceled or timed out. If the context is canceled or times out, it returns an error. It also checks if the item exists before deleting it.
func (SafeMapCache) Get ¶
Get retrieves an item from the cache. It uses a read lock to ensure thread safety. It checks if the item exists and if it has expired. If the item is found and not expired, it returns the value. If the item is not found or expired, it returns an error. The context is used to check for cancellation or timeout. If the context is canceled or times out, it returns an error.
func (SafeMapCache) Len ¶
func (c SafeMapCache) Len() int
Len returns an approximate of the number of items in the cache. It relies on a counter that is updated when items are added or deleted. This is not a precise count, but it is efficient and fast.
Example:
cache := NewSafeMap[string, any]() // GC every minute cache.SetWithTTL(context.Background(), "key", "value", 10*time.Second) fmt.Print(cache.Len()) // 1 // Wait for 20 seconds time.Sleep(20 * time.Second) fmt.Print(cache.Len()) // 1 (item is expired but still counted) // Wait for GC time.Sleep(60 * time.Second) fmt.Print(cache.Len()) // 0
func (SafeMapCache) Set ¶
Set stores an item in the cache with no expiration. It uses a write lock to ensure thread safety. It checks if the context is canceled or timed out. If the context is canceled or times out, it returns an error.
func (SafeMapCache) SetWithTTL ¶
SetWithTTL stores an item in the cache with a specified expiration time. It uses a write lock to ensure thread safety. It checks if the context is canceled or timed out. If the context is canceled or times out, it returns an error. It also checks if the TTL is valid (greater than 0).
type SafeMapCacheOption ¶
type SafeMapCacheOption[K comparable, V any] func(*safeMapCache[K, V])
func WithDefaultTTL ¶
func WithDefaultTTL[K comparable, V any](ttl time.Duration) SafeMapCacheOption[K, V]
WithDefaultTTL sets the default time-to-live (TTL) for items in the cache. If the TTL is set to 0, items will not expire. The default TTL is 0, meaning items will not expire by default.
func WithGCInterval ¶
func WithGCInterval[K comparable, V any](interval time.Duration) SafeMapCacheOption[K, V]
WithGCInterval sets the interval for the garbage collector. The garbage collector will run at this interval to delete expired items. The default interval is 5 minutes. It cannot be lower than 5 seconds.
func WithMaxSize ¶
func WithMaxSize[K comparable, V any](size int) SafeMapCacheOption[K, V]
WithMaxSize sets the maximum size of the cache. If the cache exceeds this size, it will not accept new items. The default size is 10000.