mirror of
https://github.com/redis/go-redis.git
synced 2025-07-28 06:42:00 +03:00
feat: implement client-side caching with Redis invalidation support
Add comprehensive client-side caching functionality that leverages the push notification infrastructure for automatic cache invalidation. Core Features: - Local in-memory cache with configurable size and TTL - Automatic Redis CLIENT TRACKING integration - Real-time cache invalidation via push notifications - LRU eviction policy for memory management - Thread-safe operations with RWMutex - Comprehensive statistics and monitoring API Components: - ClientSideCache: Main cache implementation - ClientSideCacheOptions: Configuration options - Client integration methods: EnableClientSideCache, DisableClientSideCache - Convenience methods: CachedGet, CachedSet, CachedDel - Statistics: GetStats with hits, misses, evictions, hit ratio Implementation Details: - Uses existing push notification system for invalidation - Integrates with Redis CLIENT TRACKING (RESP3 required) - Supports BCAST mode for prefix-based tracking - Non-blocking invalidation processing - Graceful fallback to Redis on cache misses - Automatic cleanup on client close Benefits: - Significant performance improvements for read-heavy workloads - Reduced Redis server load and network traffic - Automatic cache coherence with real-time invalidation - Transparent integration with existing Redis operations - Zero configuration required (sensible defaults) Test Coverage: - Comprehensive unit tests for all cache operations - Integration tests with real Redis instances - Edge cases: expiration, eviction, invalidation - Statistics verification and cache management - Error handling and graceful degradation Example Usage: ```go // Enable client-side caching client.EnableClientSideCache(&redis.ClientSideCacheOptions{ MaxSize: 1000, DefaultTTL: 5 * time.Minute, }) // Use cached operations value, err := client.CachedGet(ctx, "key").Result() err = client.CachedSet(ctx, "key", "value", time.Hour).Err() ``` Files Added: - client_side_cache.go: Core implementation - client_side_cache_test.go: Comprehensive tests - examples/client-side-cache/: Working example with documentation Integration: - Leverages existing push notification infrastructure - Updates shouldSkipNotification filtering (invalidate now processed) - Maintains backward compatibility - No breaking changes to existing APIs
This commit is contained in:
75
redis.go
75
redis.go
@ -210,6 +210,9 @@ type baseClient struct {
|
||||
|
||||
// Push notification processing
|
||||
pushProcessor PushNotificationProcessorInterface
|
||||
|
||||
// Client-side cache for automatic caching with Redis invalidation
|
||||
clientSideCache *ClientSideCache
|
||||
}
|
||||
|
||||
func (c *baseClient) clone() *baseClient {
|
||||
@ -835,11 +838,83 @@ func (c *Client) RegisterPushNotificationHandler(pushNotificationName string, ha
|
||||
return c.pushProcessor.RegisterHandler(pushNotificationName, handler, protected)
|
||||
}
|
||||
|
||||
// UnregisterPushNotificationHandler unregisters a handler for a specific push notification name.
|
||||
// Returns an error if no handler is registered for this push notification name or if the handler is protected.
|
||||
func (c *Client) UnregisterPushNotificationHandler(pushNotificationName string) error {
|
||||
// Check if we have a processor that supports unregistration
|
||||
if processor, ok := c.pushProcessor.(interface {
|
||||
UnregisterHandler(pushNotificationName string) error
|
||||
}); ok {
|
||||
return processor.UnregisterHandler(pushNotificationName)
|
||||
}
|
||||
return fmt.Errorf("push notification processor does not support unregistration")
|
||||
}
|
||||
|
||||
// GetPushNotificationProcessor returns the push notification processor.
|
||||
func (c *Client) GetPushNotificationProcessor() PushNotificationProcessorInterface {
|
||||
return c.pushProcessor
|
||||
}
|
||||
|
||||
// EnableClientSideCache enables client-side caching with Redis invalidation support.
|
||||
// This creates a local cache that automatically invalidates entries when Redis sends
|
||||
// invalidation notifications through CLIENT TRACKING.
|
||||
func (c *Client) EnableClientSideCache(opts *ClientSideCacheOptions) error {
|
||||
if c.clientSideCache != nil {
|
||||
return fmt.Errorf("client-side cache is already enabled")
|
||||
}
|
||||
|
||||
cache, err := NewClientSideCache(c, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.clientSideCache = cache
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableClientSideCache disables client-side caching and cleans up resources.
|
||||
func (c *Client) DisableClientSideCache() error {
|
||||
if c.clientSideCache == nil {
|
||||
return nil // Already disabled
|
||||
}
|
||||
|
||||
err := c.clientSideCache.Close()
|
||||
c.clientSideCache = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// GetClientSideCache returns the client-side cache if enabled, nil otherwise.
|
||||
func (c *Client) GetClientSideCache() *ClientSideCache {
|
||||
return c.clientSideCache
|
||||
}
|
||||
|
||||
// CachedGet retrieves a value using client-side caching if enabled, otherwise falls back to regular Get.
|
||||
// This is a convenience method that automatically uses the cache when available.
|
||||
func (c *Client) CachedGet(ctx context.Context, key string) *StringCmd {
|
||||
if c.clientSideCache != nil {
|
||||
return c.clientSideCache.Get(ctx, key)
|
||||
}
|
||||
return c.Get(ctx, key)
|
||||
}
|
||||
|
||||
// CachedSet stores a value using client-side caching if enabled, otherwise falls back to regular Set.
|
||||
// This is a convenience method that automatically updates the cache when available.
|
||||
func (c *Client) CachedSet(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd {
|
||||
if c.clientSideCache != nil {
|
||||
return c.clientSideCache.Set(ctx, key, value, expiration)
|
||||
}
|
||||
return c.Set(ctx, key, value, expiration)
|
||||
}
|
||||
|
||||
// CachedDel deletes keys using client-side caching if enabled, otherwise falls back to regular Del.
|
||||
// This is a convenience method that automatically updates the cache when available.
|
||||
func (c *Client) CachedDel(ctx context.Context, keys ...string) *IntCmd {
|
||||
if c.clientSideCache != nil {
|
||||
return c.clientSideCache.Del(ctx, keys...)
|
||||
}
|
||||
return c.Del(ctx, keys...)
|
||||
}
|
||||
|
||||
// GetPushNotificationHandler returns the handler for a specific push notification name.
|
||||
// Returns nil if no handler is registered for the given name.
|
||||
func (c *Client) GetPushNotificationHandler(pushNotificationName string) PushNotificationHandler {
|
||||
|
Reference in New Issue
Block a user