🐹 🗄️ Advanced Caching Patterns with TuskLang & Go
🗄️ Advanced Caching Patterns with TuskLang & Go
Introduction
Caching is the secret weapon of high-performance systems. TuskLang and Go let you implement sophisticated caching patterns with config-driven strategies, multi-level caches, intelligent invalidation, and distributed coordination.Key Features
- Multi-level caching (L1, L2, L3) - Cache invalidation strategies - Cache warming and preloading - Distributed caching with Redis/Memcached - Cache-aside, write-through, write-behind patterns - Cache compression and optimizationExample: Multi-Level Cache Config
[cache]
l1_backend: memory
l1_size: @env("L1_CACHE_SIZE", "100MB")
l2_backend: redis
l2_uri: @env.secure("REDIS_URI")
l3_backend: database
l3_query: @query("SELECT * FROM cache_store WHERE key = ?")
compression: @env("CACHE_COMPRESSION", true)
metrics: @metrics("cache_hit_rate", 0)
Go: Multi-Level Cache Implementation
package cacheimport (
"context"
"encoding/json"
"sync"
"time"
"github.com/go-redis/redis/v8"
"github.com/klauspost/compress/gzip"
)
type MultiLevelCache struct {
l1 *sync.Map
l2 *redis.Client
l3 Database
stats *CacheStats
}
type CacheStats struct {
l1Hits int64
l2Hits int64
l3Hits int64
misses int64
mu sync.RWMutex
}
func (c *MultiLevelCache) Get(ctx context.Context, key string) (interface{}, error) {
// Try L1 cache first
if val, ok := c.l1.Load(key); ok {
atomic.AddInt64(&c.stats.l1Hits, 1)
return val, nil
}
// Try L2 cache (Redis)
val, err := c.l2.Get(ctx, key).Result()
if err == nil {
// Store in L1 for next time
c.l1.Store(key, val)
atomic.AddInt64(&c.stats.l2Hits, 1)
return val, nil
}
// Try L3 cache (Database)
if val, err := c.l3.Get(ctx, key); err == nil {
// Store in L2 and L1
c.l2.Set(ctx, key, val, time.Hour)
c.l1.Store(key, val)
atomic.AddInt64(&c.stats.l3Hits, 1)
return val, nil
}
atomic.AddInt64(&c.stats.misses, 1)
return nil, ErrCacheMiss
}
Cache Invalidation Strategies
type InvalidationStrategy interface {
Invalidate(ctx context.Context, pattern string) error
}type TimeBasedInvalidation struct {
ttl time.Duration
}
func (t *TimeBasedInvalidation) Invalidate(ctx context.Context, pattern string) error {
// Set TTL for matching keys
keys, err := redis.Keys(ctx, pattern).Result()
if err != nil {
return err
}
for _, key := range keys {
redis.Expire(ctx, key, t.ttl)
}
return nil
}
type EventBasedInvalidation struct {
eventBus EventBus
}
func (e *EventBasedInvalidation) Invalidate(ctx context.Context, event string) error {
// Publish invalidation event
return e.eventBus.Publish(ctx, "cache:invalidate", event)
}
Cache Warming
func (c *MultiLevelCache) WarmCache(ctx context.Context, patterns []string) error {
for _, pattern := range patterns {
// Preload frequently accessed data
data, err := c.l3.GetPattern(ctx, pattern)
if err != nil {
continue
}
for key, value := range data {
c.l2.Set(ctx, key, value, time.Hour)
c.l1.Store(key, value)
}
}
return nil
}
Cache-Aside Pattern
func (c *MultiLevelCache) GetOrSet(ctx context.Context, key string, loader func() (interface{}, error)) (interface{}, error) {
// Try cache first
if val, err := c.Get(ctx, key); err == nil {
return val, nil
}
// Load from source
val, err := loader()
if err != nil {
return nil, err
}
// Store in cache
c.Set(ctx, key, val)
return val, nil
}
Write-Through Pattern
func (c *MultiLevelCache) WriteThrough(ctx context.Context, key string, value interface{}) error {
// Write to all cache levels and source
if err := c.l3.Set(ctx, key, value); err != nil {
return err
}
c.l2.Set(ctx, key, value, time.Hour)
c.l1.Store(key, value)
return nil
}
Write-Behind Pattern
type WriteBehindCache struct {
cache *MultiLevelCache
queue chan WriteOperation
workers int
}type WriteOperation struct {
Key string
Value interface{}
}
func (w *WriteBehindCache) Set(ctx context.Context, key string, value interface{}) error {
// Write to cache immediately
w.cache.Set(ctx, key, value)
// Queue for background write to source
select {
case w.queue <- WriteOperation{Key: key, Value: value}:
return nil
default:
return ErrQueueFull
}
}
func (w *WriteBehindCache) StartWorkers() {
for i := 0; i < w.workers; i++ {
go w.worker()
}
}
func (w *WriteBehindCache) worker() {
for op := range w.queue {
// Write to source in background
w.cache.l3.Set(context.Background(), op.Key, op.Value)
}
}
Cache Compression
func CompressCache(data []byte) ([]byte, error) {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}func DecompressCache(data []byte) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
defer gz.Close()
return ioutil.ReadAll(gz)
}