Skip to content

Instantly share code, notes, and snippets.

@mohashari
Created March 14, 2026 15:12
Show Gist options
  • Select an option

  • Save mohashari/bee3005bde7d73e686109a8c04dc8397 to your computer and use it in GitHub Desktop.

Select an option

Save mohashari/bee3005bde7d73e686109a8c04dc8397 to your computer and use it in GitHub Desktop.
Code snippets — Redis Caching Strategies
func UpdateUser(ctx context.Context, user *User) error {
// 1. Update database
if err := db.UpdateUser(ctx, user); err != nil {
return err
}
// 2. Update cache immediately
cacheKey := fmt.Sprintf("user:%s", user.ID)
data, _ := json.Marshal(user)
redis.Set(ctx, cacheKey, data, 15*time.Minute)
return nil
}
# Use colon-separated namespaces
user:{id}
user:{id}:posts
product:{id}:variants
session:{token}
# Include version for schema changes
v2:user:{id}
# Include tenant for multi-tenant apps
tenant:{tenant_id}:user:{user_id}
// Cache with TTL
redis.Set(ctx, "product:42", data, 5*time.Minute)
// Use longer TTLs for stable data
redis.Set(ctx, "config:features", data, 1*time.Hour)
cache_hit_rate = hits / (hits + misses) # Target: >90%
cache_memory_usage # Stay under 80%
eviction_rate # High = cache too small
func OnOrderStatusChanged(orderID string) {
// Invalidate all related caches
pipe := redis.Pipeline()
pipe.Del(ctx, fmt.Sprintf("order:%s", orderID))
pipe.Del(ctx, fmt.Sprintf("user:orders:%s", order.UserID))
pipe.Del(ctx, "dashboard:summary")
pipe.Exec(ctx)
}
// Store user's cache keys in a set
func CacheWithTag(ctx context.Context, key string, tag string, data interface{}, ttl time.Duration) {
pipe := redis.Pipeline()
serialized, _ := json.Marshal(data)
pipe.Set(ctx, key, serialized, ttl)
pipe.SAdd(ctx, fmt.Sprintf("tag:%s", tag), key)
pipe.Exec(ctx)
}
// Invalidate all caches for a user
func InvalidateTag(ctx context.Context, tag string) {
tagKey := fmt.Sprintf("tag:%s", tag)
keys, _ := redis.SMembers(ctx, tagKey).Result()
if len(keys) > 0 {
redis.Del(ctx, keys...)
}
redis.Del(ctx, tagKey)
}
func GetWithStampedeProtection(ctx context.Context, key string) ([]byte, error) {
type cachedValue struct {
Data []byte `json:"data"`
Expiry time.Time `json:"expiry"`
Delta float64 `json:"delta"`
}
raw, err := redis.Get(ctx, key).Bytes()
if err != nil {
return nil, err // Cache miss
}
var cv cachedValue
json.Unmarshal(raw, &cv)
// Probabilistically recompute before expiry
ttl := time.Until(cv.Expiry).Seconds()
if -cv.Delta*math.Log(rand.Float64()) >= ttl {
return nil, nil // Trigger early recomputation
}
return cv.Data, nil
}
func GetWithLock(ctx context.Context, key string, fetch func() ([]byte, error)) ([]byte, error) {
// Try cache first
if val, err := redis.Get(ctx, key).Bytes(); err == nil {
return val, nil
}
// Acquire lock
lockKey := key + ":lock"
acquired, _ := redis.SetNX(ctx, lockKey, "1", 10*time.Second).Result()
if !acquired {
// Wait for lock holder to populate cache
time.Sleep(50 * time.Millisecond)
return GetWithLock(ctx, key, fetch)
}
defer redis.Del(ctx, lockKey)
// Double-check after acquiring lock
if val, err := redis.Get(ctx, key).Bytes(); err == nil {
return val, nil
}
// Fetch and cache
val, err := fetch()
if err != nil {
return nil, err
}
redis.Set(ctx, key, val, 5*time.Minute)
return val, nil
}
func GetUser(ctx context.Context, userID string) (*User, error) {
// 1. Check cache
cacheKey := fmt.Sprintf("user:%s", userID)
cached, err := redis.Get(ctx, cacheKey).Result()
if err == nil {
var user User
json.Unmarshal([]byte(cached), &user)
return &user, nil
}
// 2. Cache miss — query database
user, err := db.GetUser(ctx, userID)
if err != nil {
return nil, err
}
// 3. Populate cache
data, _ := json.Marshal(user)
redis.Set(ctx, cacheKey, data, 15*time.Minute)
return user, nil
}
Client → Redis (immediate) → Queue → Database (async)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment