Belajar Laravel #21: Cache & Optimasi — Belum Hitung Ulang, Perbaiki Query Dulu

Di episode #20 kita belajar memindahkan pekerjaan berat ke latar belakang lewat event dan listener. Kali ini kita naik satu level lagi: bagaimana cara membuat aplikasi Laravel tidak harus menghitung ulang hal yang sama berulang kali. Ini yang saya catat soal cache, eager loading, dan perintah optimize.

Caching: Menyimpan Hasil, Bukan Menghitung Ulang

Bayangkan saya punya laporan penjualan yang query-nya berat — butuh tiga detik untuk selesai. Kalau 100 orang membuka halaman itu dalam satu menit, database saya kerja 300 detik untuk sesuatu yang isinya kemungkinan besar sama saja. Cache adalah catatan tempel: hasil query disimpan dulu, pengunjung berikutnya tinggal membaca catatan itu tanpa menghitung ulang.

Laravel menyembunyikan soal driver di balik satu facade: Cache. Sama seperti database, driver cache bisa ditukar lewat .env tanpa mengubah kode sama sekali.

// di .env: CACHE_STORE=database (bisa file, memcached, redis, array)

use Illuminate\Support\Facades\Cache;

// ambil dari cache, kalau tidak ada baru query lalu simpan 600 detik
$laporan = Cache::remember('laporan-penjualan', now()->addSeconds(600), function () {
    return DB::table('orders')
        ->selectRaw('status, COUNT(*) as total')
        ->groupBy('status')
        ->get();
});

Cache::forget('laporan-penjualan'); // buang satu key
Cache::increment('hitungan');       // cocok untuk counter

Ini yang paling saya sukai: Cache::remember(). Saya tidak perlu menulis “cek dulu ada atau tidak” secara manual — Laravel yang mengurus logikanya. Tugas saya hanya menentukan key, lama simpan, dan cara menghasilkan nilai saat key-nya kosong.

Tagged Cache: Mengelompokkan Supaya Mudah Dibersihkan

Masalah cache: begitu datanya berubah, cache lama harus dibuang. Kalau key-nya banyak dan tersebar, saya bisa lupa membersihkan sebagian. Solusinya tagged cache — kumpulkan key dalam satu tag, lalu hapus sekaligus.

use Illuminate\Support\Facades\Cache;

$produk = Cache::tags(['produk', 'toko-' . $toko->id])
    ->remember('produk.' . $produk->id, 3600, function () use ($produk) {
        return $produk->load('kategori', 'gambar');
    });

// saat produk diubah:
Cache::tags(['produk'])->flush(); // semua cache produk ikut terhapus

Catatan penting: tagged cache hanya tersedia di driver memcached dan redis. Kalau pakai file atau database, panggilan tags() akan error. Di proyek kecil yang masih file cache, saya biasanya pakai prefix naming saja, misalnya semua key produk diawali produk..

Eager Loading: Menangkap Masalah N+1

Jujur, masalah performa yang paling sering saya temui bukan karena kurang cache — melainkan karena eager loading terlewat. Ini pola N+1 klasik: satu query untuk daftar post, lalu satu query tambahan untuk komentar tiap post. 20 post berarti 21 query, padahal bisa diselesaikan dalam 2.

// BURUK: 1 query daftar + N query komentar
foreach (Post::all() as $post) {
    echo $post->komentars->count();
}

// BAIK: eager loading — semua komentar diambil dalam 1 query tambahan
$posts = Post::with('komentars')->get();
foreach ($posts as $post) {
    echo $post->komentars->count();
}

Saya biasa menyalakan log query di proyek lokal dengan DB::enableQueryLog() atau Telescope untuk melihat berapa query benar-benar berjalan. Kalau jumlahnya tidak wajar, kemungkinan besar ada relasi yang belum di-eager. Ini materi yang nyambung dengan episode #7 tentang relationship — eager loading adalah pelengkap wajibnya.

Config, Route, dan View Cache

Di production, Laravel bisa meng-cache config dan route supaya tidak membaca file setiap request. Ini dulu sempat bikin saya bingung karena perubahannya tidak langsung kelihatan.

php artisan config:cache   # gabung semua config jadi satu file
php artisan route:cache    # cache daftar route
php artisan view:cache     # pre-compile semua blade
php artisan optimize       # jalankan semuanya sekaligus

php artisan config:clear   # buang cache saat ganti .env di production

Pelajaran pahit yang saya dapat: setelah ganti nilai .env di server production, nilainya tidak berubah — ternyata config masih ter-cache. Ingat, config:cache membaca nilai saat cache dibuat, jadi kalau .env diubah, cache harus di-clear dan di-build ulang.

Catatan Penutup

Urutan optimasi yang saya pelajari: pertama perbaiki query yang boros (eager loading, index), baru tambah cache, baru optimize file. Banyak orang langsung pasang Redis padahal query-nya sendiri masih N+1 — cache memang menutupi masalahnya, tapi masalahnya tetap ada di bawah sana. Cache adalah lapisan terakhir, bukan langkah pertama.

Di episode berikutnya kita masuk ke deployment: menyiapkan aplikasi Laravel untuk production, environment variable, dan pertimbangan shared hosting versus VPS.

Sumber

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses