Pendahuluan

Masalah utama ketika worker queue CodeIgniter 4 direstart adalah cache Redis yang masih memegang data lama sementara job baru sudah berjalan, menghasilkan duplikasi proses atau API response tidak konsisten. Dalam dua paragraf berikut, kita langsung bahas solusi operasional: sinkronisasi cache Redis melalui invalidasi tegas, locking job sebelum eksekusi, serta verifikasi hasil untuk menjaga konsistensi API.

Strategi ini memastikan worker tahu cache mana yang sudah usang, job dijalankan sekali, dan API kembali ke keadaan stabil tanpa respon kadaluarsa atau data setengah jadi.

1. Mengatur Redis Service dan Worker di CodeIgniter 4

Konfigurasi Redis di Config/Cache.php

Pastikan Config/Cache.php mendefinisikan Redis dengan parameter lingkungan yang konsisten antara request HTTP dan worker queue. Contoh:

public $redis = [
    'host'     => env('REDIS_HOST', '127.0.0.1'),
    'password' => env('REDIS_PASSWORD', null),
    'port'     => env('REDIS_PORT', 6379),
    'timeout'  => 1.0,
    'database' => 0,
    'scheme'   => 'tcp',
    'options'  => [
        'parameters' => [
            'retry_interval' => 100,
        ],
    ],
];

Perhatikan retry_interval untuk menghadapi flapping koneksi. Worker queue (misalnya dengan library Chronos) harus memuat konfigurasi yang sama agar kunci cache yang dibuat selama enqueue bisa diakses saat worker dieksekusi ulang.

Pengaturan Worker

Worker sebaiknya memuat dependency cache dengan service container, bukan instansiasi manual, sehingga semua state cache valid pada saat restart. Pastikan worker mengikuti pattern berikut:

  • Ketika menerima job, cek adanya flag validasi cache (misalnya cache:job:).
  • Gunakan lock() pada cache sebelum memproses job agar tidak ada worker lain mengeksekusi job serupa.
  • Setelah selesai, perbarui response cache dan lepaskan lock.

2. Strategi Invalidasi Cache dan Locking

Invalidasi Cache Setelah Restart

Saat worker restart, adanya key dari run sebelumnya dapat membuat job menganggap sudah selesai. Solusi praktis:

  1. Gunakan prefix lengkap seperti queue:cache:{job_id} agar invalidasi selektif.
  2. Sisipkan versi job di cache (misalnya version) dan saat worker start ulang, bandingkan versi job terbaru dari database dengan versi cache. Jika beda, hapus cache lama dan paksa reload.

Locking Job: setnx atau Redlock

Gunakan SETNX untuk memastikan hanya satu worker menjalankan job. Pola umum:

$lockKey = "lock:job:{$jobId}";
$obtained = cache()->getRedis()->setnx($lockKey, $workerId);
if (! $obtained) {
    // gunakan TTL untuk mencegah deadlock
    cache()->getRedis()->expire($lockKey, 30);
    return;
}

Untuk deployment multi-node, implementasikan Redlock: simpan lock di beberapa instance Redis dan pastikan mayoritas mengonfirmasi kepemilikan sebelum eksekusi. Jika lock gagal, worker harus rollback status job (lihat bagian rollback) dan tidak memproses job lagi sampai lock berhasil.

3. Konsistensi Job dan Response API

Validasi Status Job

Pastikan job menyimpan status di database dan cache. Setelah worker selesai, update status completed beserta timestamp. API yang menanyakan status job harus membaca cache terlebih dahulu, dan jika versi cache tidak cocok (misalnya flag cache:version lebih lama dari versi DB), maka cache dihapus dan data diambil ulang dari DB.

Response Akhir untuk Klien

Jika job berkaitan dengan API yang harus memberikan response konsisten, ambil data final hanya setelah:

  • lock dilepas;
  • cache telah sinkron dengan status job terbaru;
  • tidak ada ulang job (dicek lewat status job).

Gunakan middleware cache invalidation pada endpoint API inti agar response setelah restart worker tidak merujuk ke cache usang.

4. Monitoring Queue dan Cache

Untuk memastikan sinkronisasi tetap terjaga, monitoring wajib dilakukan pada metrik berikut:

  • queue:length – lihat jumlah job tertunda dan pastikan tidak menumpuk setelah restart.
  • cache:hits vs cache:misses – untuk mendeteksi cache stale.
  • lock:acquire dan lock:fail – rasio lock gagal membantu menentukan apakah perlu perbaikan TTL atau retry logic.

Gunakan exporter seperti Redis Exporter dan integrasikan ke Prometheus/Grafana. Untuk queue worker, pasang logging terpusat agar event restart, lock failure, dan invalidasi tercatat detail.

5. Rollback Saat Lock Gagal

Jika setnx gagal (misalnya karena job masih dijalankan worker lain) dan worker sudah memodifikasi sebagian data, jalankan rollback:

  • Reset status job ke pending beserta timestamp terakhir.
  • Hapus cache parsial agar API tidak membaca state setengah jadi.
  • Laporkan kejadian ke monitoring (misalnya counter queue:lock:rollback).

Rollback memastikan tidak ada job yang dianggap selesai atau cache yang membuat API membaca informasi inkonsisten. Kunci penting: jangan biarkan job lanjut tanpa lock valid.

6. Checklist Operasional

Checklist berikut membantu tinjauan harian:

  • 🟢 Redis service terhubung, latency normal (< 5 ms).
  • 🟢 Jumlah job queue (pending/wip/completed) seimbang sesuai SLA.
  • 🟢 Tidak ada lock timeout yang berulang (monitor lock:expire events).
  • 🟢 Cache version terbaru sama dengan status job di DB.
  • 🟢 Log restart worker dan invalidasi cache dibersihkan setiap hari.

7. Troubleshooting

Cache Tetap Membaca Data Lama Setelah Restart

Periksa apakah key cache mempunyai version mismatch. Jika iya, tambah invalidation script di pipeline restart worker yang memanggil DEL prefix:queue:cache:* untuk job yang aktif saat restart.

Lock Gagal Padahal Tidak Ada Job Lain

Jika log menunjukkan lock gagal tapi tidak ada worker lain, pastikan TTL lock tidak terlalu pendek. Lock harus memiliki TTL sedikit lebih panjang dari waktu eksekusi rata-rata job.

API Response Tidak Sinkron walau Worker Selesai

Periksa mekanisme cache invalidation endpoint API dan pastikan setelah job selesai, cache queue:api: diganti. Bisa juga tambahkan header X-Job-Version untuk mengecek kesesuaiannya.

Penutup

Dengan invalidasi cache tegas, locking yang andal, monitoring metrik queue/cache, serta rollback yang siap dipakai saat lock gagal, CodeIgniter 4 dapat menjaga integritas job dan respons API meski worker queue direstart. Terapkan checklist operasional dan troubleshooting untuk memastikan sinkronisasi Redis tetap terjaga setiap hari.