Mempercepat aplikasi dengan cache: memilih driver File Redis atau Memcached, menerapkan page cache, dan query caching, lalu membangun cache list API dengan strategi invalidation yang benar agar data tidak basi.

Semakin besar aplikasi, semakin terasa pertanyaan: kenapa halaman ini lambat? Jawabannya biasanya bukan server lemah, tapi data yang dihitung ulang padahal hasilnya sama. Di episode 14 ini kita memecahkan masalah itu dengan caching — menyimpan hasil yang mahal untuk dipakai ulang.
Mengapa penting? Karena latensi adalah pengalaman pengguna: response 500ms vs 50ms menentukan apakah user kembali atau pindah. Dan lebih jauh lagi, cache mengurangi beban database — aset yang paling mahal di aplikasi web. Di episode ini kalian akan belajar memilih driver, memakai API cache, dan membangun strategi invalidation yang benar.
Konfigurasi cache di app/Config/Cache.php:
public string $handler = 'file';
public string $backupHandler = 'file';
public $file = [
'storePath' => WRITEPATH . 'cache',
];
public $redis = [
'host' => '127.0.0.1',
'password' => null,
'port' => 6379,
'timeout' => 0,
'database' => 0,
];| Driver | Kelebihan | Cocok untuk |
|---|---|---|
file | Tanpa setup, langsung jalan | Development, aplikasi single-server |
redis | Cepat, shared antar server, TTL akurat | Produksi multi-server, session Redis |
memcached | Sangat cepat, distribusi sederhana | Produksi dengan Memcached |
Note
Untuk development, file sudah cukup. Untuk produksi (episode 23-24), kita akan pindah ke Redis — alasannya: cache dibagikan antar instance aplikasi sehingga semua server membaca data yang sama, dan eviction (pembuangan item) berjalan otomatis.
\Config\Services::cache() mengembalikan instance cache sesuai handler:
$cache = \Config\Services::cache();
// SET - simpan dengan TTL 300 detik
$cache->save('key', $value, 300);
// GET - ambil; null jika belum ada / kedaluwarsa
$value = $cache->get('key');
// DELETE - hapus satu item
$cache->delete('key');
// CLEAN - bersihkan semua
$cache->clean();
// HAS - cek keberadaan
$cache->has('key');Nilai yang disimpan bisa berupa apa pun yang bisa di-serialize PHP: array, objek, atau string. Seluruh API konsisten antar driver — berpindah dari file ke Redis tidak mengubah kode aplikasi.
Pola paling umum adalah cache-aside: baca cache, jika kosong hitung lalu simpan.
$key = 'posts_list_page_1';
$cache = \Config\Services::cache();
$posts = $cache->get($key);
if ($posts === null) {
$posts = $postModel->where('status', 'published')
->orderBy('created_at', 'DESC')
->findAll();
$cache->save($key, $posts, 300);
}Request pertama lambat (query database), request berikutnya cepat (baca cache) sampai TTL 300 detik habis.
Warning
Cache-aside menambah satu sumber kebenaran: cache dan database bisa tidak sinkron. TTL adalah "jangkar" yang memastikan data akhirnya terbarui, tapi untuk data yang harus selalu segar, TTL pendek atau invalidation eksplisit adalah jawabannya.
Untuk halaman penuh yang sama untuk semua user, CodeIgniter bisa cache seluruh output HTML:
public function index(): string
{
$this->cachePage(60); // cache output 60 menit
$data = [
'title' => 'Beranda',
'posts' => $postModel->where('status', 'published')->findAll(),
];
return view('home', $data);
}Request berikutnya tidak menjalankan controller sama sekali — framework langsung menyajikan HTML dari cache. Sangat cocok untuk halaman publik yang jarang berubah. Tapi jangan dipakai untuk halaman personal (dashboard per-user) karena semua user akan melihat halaman user pertama.
Untuk API list, kita gabungkan cache-aside dengan invalidation — menghapus cache saat data berubah:
public function index(): \CodeIgniter\HTTP\ResponseInterface
{
$key = 'api_posts_list';
$cache = \Config\Services::cache();
$posts = $cache->get($key);
if ($posts === null) {
$posts = $this->model->where('status', 'published')
->orderBy('created_at', 'DESC')
->findAll();
$cache->save($key, $posts, 600);
}
return $this->respond($posts);
}
public function create(): \CodeIgniter\HTTP\ResponseInterface
{
$rules = ['title' => 'required', 'slug' => 'required|is_unique[posts.slug]'];
if (! $this->validate($rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$id = $this->model->insert($this->request->getPost());
\Config\Services::cache()->delete('api_posts_list'); // invalidation
return $this->respondCreated(['id' => $id]);
}Setiap kali post dibuat, diubah, atau dihapus, cache api_posts_list dihapus — request berikutnya menghitung ulang. Hasilnya: data selalu segar, tanpa menunggu TTL, dan query database hanya terjadi saat data berubah.
Tip
Strategi kunci cache yang jelas membantu: api_posts_list, post_detail_{$id}, page_home. Dengan konvensi nama, invalidation jadi mudah — hapus api_posts_list saat list berubah, hapus post_detail_{$id} saat post itu berubah.
Jangan asal percaya cache membuat cepat — ukur:
time curl -s http://localhost:8080/api/v1/posts > /dev/null
# pertama (cache cold): ~180ms
# kedua (cache warm): ~25msPerbedaan nyata terlihat ketika database dan cache terpisah. Di episode 24 kita akan memakai load testing (ab, hey) untuk membandingkan throughput sebelum/sesudah optimisasi — cache adalah salah satu strategi utamanya.
file untuk dev, redis/memcached untuk produksi.\Config\Services::cache(): save, get, delete, clean, has.cachePage()) untuk halaman publik yang sama semua user.Inti yang harus dibawa pulang:
Di episode 15 selanjutnya kita akan membahas logging & debugging — konfigurasi log, level dan structured log, Debug Toolbar, serta workflow troubleshoot 404 dan 500. Saat error muncul, kalian akan tahu persis ke mana harus melihat!