API order service kami mulai menunjukkan respons yang lambat dan data order yang kadang tidak sinkron dengan sumber utama setelah terjadi spike lalu lintas. Dari log dan metrik langsung terlihat bahwa cache Redis yang seharusnya mempercepat respons terus mengalami expired sebelum data baru dipopulasikan, sehingga klien terjebak menunggu fetch ulang. Artikel ini menguraikan investigasi dari gejala sampai perbaikan untuk mencegah cache Redis expired yang merusak SLA.

Konteks Sistem API Order

Order service adalah bagian dari arsitektur microservice dengan pola CQRS: data order disimpan pada database utama, kemudian diambil melalui read replica untuk kebutuhan API. Agar latency rendah, setiap permintaan GET order mencoba membaca dari cache Redis yang menyimpan payload JSON lengkap dengan TTL sekitar 30 detik.

Request flow sederhana:

  1. API menerima permintaan detail order.
  2. Service mengecek Redis: jika ada entry, langsung dikembalikan.
  3. Jika cache miss, service query database, kemudian menyimpan hasilnya ke Redis dengan expire.

Biasanya pola ini berhasil karena traffic membaca order relatif stabil. Namun saat ada promosi flash sale, volume spike membuat TTL menjadi titik kegagalan.

Gejala, Log, dan Metrik

Gejala awal yang dilaporkan tim produk adalah:

  • Respons API order berlangsung > 500 ms padahal biasanya < 150 ms.
  • Beberapa API mengembalikan order kosong padahal data sudah ada di database.
  • Spike error 5xx akibat timeout downstream.

Jejak log menunjukkan pola berikut:

  • Cache hit ratio turun drastis pada periode spike, sementara throughput Redis tetap tinggi.
  • Log middleware mencatat banyak cache miss beruntun untuk key yang sama.
  • Log worker yang memuat data ke cache menyatakan set berhasil tapi diikuti oleh expire event segera.

Metrik latency Redis meningkat karena banyak klien menunggu fetch ulang. Grafik TTL bucket dari observabilitas Redis menunjukkan baris angka TTL mendekati nol setiap beberapa detik, yang mengindikasikan TTL ditetapkan terlalu pendek atau tidak di-refresh dengan benar.

Analisis Root Cause: Cache Redis Expired

Setelah menelusuri token request, root cause teridentifikasi sebagai berikut:

  • TTL sebesar 30 detik terlalu pendek untuk periode spike; banyak permintaan ke key yang sama terjadi dalam jarak waktu lebih singkat, sehingga cache resmi expired sebelum terdapat kesempatan untuk cache hit berikutnya.
  • Race condition saat cache miss membuat beberapa worker memanggil database secara bersamaan, menambah beban dan memicu lebih banyak TTL set ulang.
  • Expired event memicu fallback synchronous ke database tanpa mekanisme grace period atau provisioning ulang otomatis.

Penyebab utamanya bukan Redis bermasalah, melainkan konfigurasi TTL tidak disesuaikan dengan pola traffic. Selain itu tidak ada strategi fallback yang menangani cache miss dalam jumlah besar.

Langkah Perbaikan

1. Penyesuaian TTL Berdasarkan Pola Traffic

TTL harus reflektif terhadap bobot request. Kami menaikkan TTL menjadi 2 menit dengan pendekatan adaptive, yakni jika jumlah hit per key melebihi threshold, TTL di-refresh menggunakan EXPIRE tanpa harus re-set data. Contoh implementasi:

// pseudo-code untuk memperpanjang TTL jika cache masih valid
const KEY = `order:${orderId}`;
const cached = await redis.get(KEY);
if (cached) {
  await redis.expire(KEY, 120); // refresh TTL jika request padat
  return JSON.parse(cached);
}

Perpanjangan ini diterapkan hanya jika data masih dianggap relevan; untuk order yang berubah sering (misalnya status update), TTL tetap pendek tapi disinkronisasi dari event source.

2. Fallback Data Saat Cache Miss Besar

Kami menambahkan circuit breaker ringan yang:

  • Mendeteksi lonjakan cache miss per key (misal > 5 dalam 5 detik).
  • Menunda fetch database selama 50 ms dan hanya satu worker yang mengambil data.
  • Worker lain menggunakan nilai fallback sementara (misalnya cache copy yang sedikit usang) atau mengirim 503 dengan header Retry-After untuk mencegah thundering herd.

Implementasi fallback dapat memanfaatkan Redis Streams atau key supplemental yang berisi data snapshot lama untuk response cepat saat data terbaru belum tersedia.

3. Observabilitas untuk TTL dan Cache Behavior

Perbaikan TTL tidak lengkap tanpa monitoring yang memadai. Kami menambahkan instrumen berikut:

  • Histogram latency Redis per endpoint, agar bisa melihat dampak cache hit versus DB hit.
  • Counter untuk cache miss, cache refresh, dan cache expire.
  • Trace distributed request untuk menunjukkan alur saat cache miss menyebabkan timeout downstream.

Alert diatur jika cache miss meningkat lebih dari 30% dalam 5 menit beruntun, atau latency Redis melebihi 250 ms.

Pelajaran bagi Developer

Beberapa pelajaran penting dari studi kasus ini:

  • TTL tidak boleh statis ketika traffic memiliki pola burst; TTL yang terlalu pendek menyebabkan cache menjadi sumber ketidakstabilan.
  • Jaga fallback untuk mencegah sistem menjadi tidak responsif saat cache tidak tersedia — penanganan thundering herd adalah kebutuhan, bukan kemewahan.
  • Observabilitas mempermudah diagnosis; tanpa metrik TTL, kita hanya melihat gejala, bukan parameter yang sebenarnya salah.

Dengan menggabungkan penyesuaian TTL, fallback yang cerdas, dan observabilitas lengkap, API order service kembali ke SLA-nya dan lebih tahan terhadap beban mendadak.