Lewati ke konten utama
menjadi.dev
Chapter 17.14 Backend Menengah

Redis: Caching Strategy dan Session Management

Implementasi caching dan session store di aplikasi backend

Tujuan Pembelajaran

  • Bisa implementasi cache-aside dan write-through
  • Mengerti cache invalidation strategies
  • Bisa membuat session store dengan Redis

Analogi

Diagram

      Caching Strategies:
├── Cache-Aside (Lazy Load)
├── Write-Through (Sync)
├── Write-Behind (Async)
└── Cache Invalidation

Session Store:
SET session:abc123 '{"userId":1}' EX 3600
    

Caching adalah use case paling umum Redis — strategi yang tepat sangat penting

Penjelasan Konsep

Caching adalah tentang trade-off: kamu menukar freshness data dengan kecepatan. Strategi caching yang tepat tergantung pada seberapa sering data berubah dan seberapa penting data yang selalu fresh.

Cache-Aside (Lazy Loading)

Pola paling umum: cek cache dulu, kalau miss baru ke database.

1. App minta data
2. Cek Redis → HIT? return data
3.

MISS? Query database
4. Simpan ke Redis
5. Return data

Pros: Simple, tidak ada write penalty Cons: Cache miss tetap lambat (db query), risk stale data

Write-Through

Setiap write ke database juga write ke cache secara synchronous.

1.

App write data
2. Simpan ke database
3. Simpan ke cache
4. Return success

Pros: Cache selalu fresh, read cepat Cons: Write lebih lambat (2x write), cache bisa membesar tanpa batas

Write-Behind (Write-Back)

Write ke cache dulu, database di-update secara async.

1.

App write data
2. Simpan ke cache
3. Return success (kepada user)
4. Async: update database

Pros: Write sangat cepat, throughput tinggi Cons: Risk data loss kalau crash sebelum db write

Cache Invalidation

Invalidasi cache lebih penting daripada caching itu sendiri.

Strategi:

  1. TTL (Time To Live): Set expire otomatis

    SET cache:user:1 $data EX 300  # Expire 5 menit
  2. Active Invalidation: Hapus cache saat data berubah

    // Saat user di-update
    UPDATE users SET name = 'Baru' WHERE id = 1;
    DEL cache:user:1;  // Invalidate cache
  3. Pattern-based Invalidation: Hapus multiple keys

    KEYS cache:user:*  # Find all user cache
    DEL cache:user:1 cache:user:2 ...

Session Store

Redis sangat cocok untuk session store karena:

  • Cepat (sub-milidetik read/write)
  • Auto-expire (session timeout otomatis)
  • Shared across servers (semua server akses Redis yang sama)
// Session flow
// 1.

User login → buat session
const sessionId = crypto.randomUUID();
await redis.setex(`session:${sessionId}`, 3600, JSON.stringify({ userId: 1, role: 'admin' }));
// Set cookie dengan sessionId

// 2. Request berikutnya → validasi session
const session = await redis.get(`session:${sessionId}`);
if (!session) return 401; // Session expired
const user = JSON.parse(session);

Contoh Kode

typescript
import { createClient } from 'redis';

const redis = createClient({ url: 'redis://localhost:6379' });
await redis.connect();

// Cache-aside implementation
class UserCache {
  private ttl = 300; // 5 menit
  
  async getUser(id: number) {
    // 1. Cek cache
    const cached = await redis.get(`user:${id}`);
    if (cached) {
      console.log('Cache HIT');
      return JSON.parse(cached);
    }
    
    // 2. Cache miss → query DB
    console.log('Cache MISS');
    const user = await db.select().from(users).where(eq(users.id, id));
    if (!user) return null;
    
    // 3. Simpan ke cache
    await redis.setex(`user:${id}`, this.ttl, JSON.stringify(user));
    return user;
  }
  
  async invalidateUser(id: number) {
    await redis.del(`user:${id}`);
  }
  
  async updateUser(id: number, data: any) {
    // Write-through: update DB + cache
    const [user] = await db.update(users).set(data).where(eq(users.id, id)).returning();
    await redis.setex(`user:${id}`, this.ttl, JSON.stringify(user));
    return user;
  }
}

// Session store
class SessionStore {
  private ttl = 3600; // 1 jam
  
  async create(userId: number, data: any): Promise<string> {
    const sessionId = crypto.randomUUID();
    await redis.setex(`session:${sessionId}`, this.ttl, JSON.stringify({ userId, ...data }));
    return sessionId;
  }
  
  async get(sessionId: string): Promise<any | null> {
    const data = await redis.get(`session:${sessionId}`);
    return data ? JSON.parse(data) : null;
  }
  
  async destroy(sessionId: string): Promise<void> {
    await redis.del(`session:${sessionId}`);
  }
  
  async refresh(sessionId: string): Promise<void> {
    await redis.expire(`session:${sessionId}`, this.ttl);
  }
}

Penjelasan Kode

setex = set + expire (atomic operation). Pattern cache-aside: get → miss → db → set. Session refresh memperpanjang TTL.

Prompt AI

Implementasikan rate limiting dengan Redis: track request count per IP dengan window 1 menit, limit 60 requests.

Pertanyaan Reflektif

Cache invalidation adalah 'hard problem' dalam computer science. Dokumentasikan strategi caching-mu dan kapan invalidasi terjadi.