CodeIgniter 4: Menjaga Cache Worker Queue Konsisten di Load Tinggi

Masalah utama ketika load melonjak adalah cache dan worker queue menjadi tidak sinkron: cache menyimpan data yang belum diproses, atau worker mengambil data lama saat retry gagal. Di CodeIgniter 4, Anda bisa menjaga konsistensi ini dengan kombinasi locking terdistribusi, backpressure yang terukur, retries dengan deduplikasi, dan rekonsiliasi cache saat worker gagal.

Artikel ini langsung menjelaskan pola praktis yang sudah terbukti di sistem produksi tanpa teori kosong: bagaimana mengelola locking Redis atau database, cara mengatur queue serta cache configuration, penanganan backpressure dan retry, strategi rekonsiliasi, serta apa yang perlu dimonitor dan dioperasikan ketika gangguan terjadi.

Menetapkan Dasar Consistency untuk Cache dan Worker Queue

Pertama, pahami bahwa cache tidak boleh bergerak sendiri: saat queue worker mengubah data, cache harus di-refresh dengan jaminan bahwa perubahan telah sukses diproses ataupun dapat di-roll-back. Gunakan satu sumber kebenaran dengan pola write-through atau write-behind yang terkoordinasi dengan worker queue.

Arsitekturnya bisa berupa: request menulis metadata ke cache dengan flag “pending”, job queue dipicu untuk memproses data tersebut, dan worker hanya menghapus flag setelah commit ke database. Cache reader harus memperlakukan flag pending sebagai sinyal untuk membaca dari sumber utama sampai worker selesai.

Locking Redis atau Database agar Data Tidak Bertabrakan

Locking mencegah dua worker memodifikasi cache atau queue secara simultan. Redis cocok sebagai lock terdistribusi karena menyediakan SET NX dengan expiration, tetapi Anda juga bisa menggunakan row-level lock di database jika sudah ada pool koneksi. Contoh pola Redis lock pada worker:

$lockKey = 'cache:write:' . $cacheKey;
$lockValue = session_id();
if ($redis->set($lockKey, $lockValue, ['nx', 'ex' => 30])) {
    try {
        $cache->save($cacheKey, $value, 3600);
        $queue->push('cacheProcessing', $cacheKey);
    } finally {
        if ($redis->get($lockKey) === $lockValue) {
            $redis->del($lockKey);
        }
    }
} else {
    log_message('warning', "Lock aktif untuk {$cacheKey}, job ditunda.");
    retry_later();
}

Locking memastikan job queue menyala satu-per-satu per item cache, yang penting saat cache menjadi sumber untuk banyak consumer. Pastikan expiration lock sedikit lebih pendek dari waktu maksimum kerja worker untuk menghindari deadlock.

Backpressure, Retries, dan Deduplikasi Job

Queue harus bisa menahan lonjakan request dengan menekan ekseskusi saat worker penuh. Atur opsi seperti batch size, timeout, dan retry per job. Contoh konfigurasi sederhana di app/Config/Queue.php:

public $default = [
    'handler'        => 'Redis',
    'handlerOptions' => [
        'timeout'   => 5,
        'retry'     => 3,
        'backoff'   => 200,
    ],
    'workers'        => 10,
    'maxRuntime'     => 600,
    'sleep'          => 1000,
    'retryDelay'     => 1500,
];

Gunakan backpressure dengan cara menghentikan penerimaan job baru saat backlog melebihi batas, lalu aktifkan kembali saat antrean kembali normal. Retry harus disertai deduplikasi job agar worker tidak mengerjakan item yang sudah selesai. Sebuah flag pada cache atau database bisa dipakai untuk menandai job sudah diproses: worker memeriksa flag sebelum memulai dan meng-update flag setelah berhasil.

Jangan lupa log retry yang gagal lebih dari threshold untuk mencegah retry tak berujung. Sistem notifikasi bisa memicu alert jika retry per job melebihi 5 kali dalam 10 menit.

Rekonsiliasi Cache saat Worker Gagal

Meski worker sudah diatur retry, tetap ada kemungkinan gagal total: misalnya service downstream mati. Dalam kasus ini, cache mungkin menyimpan status “pending” lama. Lakukan rekonsiliasi dengan dua pendekatan:

  • Daemon terjadwal memindai cache yang punya flag pending lebih lama dari threshold, lalu mem-trigger job recovery atau fallback langsung ke database.
  • Event sink mengumpulkan failure event dari queue dan men-defer penulisan cache ke database ketika kondisi sehat, memastikan cache akhirnya konsisten.

Jangan hanya menghapus cache saat worker gagal tanpa validasi. Worker harus mencatat status terakhir, lalu job background memutuskan apakah perlu rollback cache, requeue, atau menandai job sebagai stuck agar operator bisa intervensi.

Monitoring dan Operasional untuk Queue dan Cache

Monitoring mencegah isu konsistensi meluas. Ukur yang berikut:

  • Job duration: rata-rata dan persenil 95% untuk memastikan worker tidak overload.
  • Retry count: job dengan retry tinggi yang menandakan tenggat atau upstream bermasalah.
  • Queue depth: jumlah job yang menumpuk di Redis atau database.
  • Lock contention: frekuensi lock timeout atau kegagalan acquire.
  • Cache flag age: durasi flag pending untuk mendeteksi job yang terlantar.

Tarik metrik ke Prometheus/Grafana, dan log ke centralized logging agar analisis deadlock mudah. Misalnya, log setiap kali lock gagal diakuisisi karena worker lain masih aktif.

Checklist Operasional saat Terjadi Gangguan Queue + Cache

  1. Periksa apakah lock Redis kadaluarsa/tidak dibersihkan; jika banyak lock lama, pertimbangkan graceful restart worker.
  2. Pantau dead-letter queue atau job yang berkali-kali retry; hapus manual hanya setelah memastikan tidak ada side effect.
  3. Jalankan rekonsiliasi cache dengan membandingkan cache flag dan data utama.
  4. Skalakan worker sementara jika antrean backlog akibat latency downstream.
  5. Gunakan alert untuk backpressure threshold agar sistem tidak terus-terusan menolak request.

Dengan checklist ini operator bisa segera membedakan antara masalah performa versus konsistensi data.

Kesimpulannya, mengatasi tinggi traffic di CodeIgniter 4 membutuhkan koordinasi cache dan queue melalui locking Redis/database, backpressure dan retry kontrol, rekonsiliasi saat failure, serta observability yang memadai. Pendekatan berlapis seperti ini menjaga cache tetap konsisten sementara worker queue terus melayani request tanpa overload.