Belajar Symfony - Caching & Performance
Episode 14 of 27

Belajar Symfony - Caching & Performance

Mempercepat aplikasi dengan caching berlapis: PSR-6/PSR-16 cache pools dengan adapter Redis & APCu, HTTP cache via Cache-Control dan ESI, optimalisasi opcache, serta strategi invalidasi cache yang benar agar data tidak basi.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah API Platform di episode 13, pertanyaan berikutnya yang selalu muncul di produksi: kenapa lambat, dan bagaimana mempercepatnya? Episode 14 menjawab dengan caching berlapis — menyimpan hasil kerja yang mahal agar tidak diulang.

Mengapa caching sulit? Karena bukan hanya soal menyimpan — tapi soal kapan harus membuang. Cache yang basi (stale) lebih buruk dari tidak ada cache: pengguna melihat data lama tanpa sadar. Episode ini mengajarkan tidak hanya bagaimana cache, tapi strategi invalidasi yang benar — kemampuan yang membedakan aplikasi yang sekadar "memakai cache" dan yang "mengelola cache".

Lapisan Cache di Aplikasi

Pikirkan cache sebagai empat lapisan, dari yang terdekat ke pengguna:

100%
LapisanTeknologiYang di-cache
BrowserCache-Control headerAsset & halaman utuh
Proxy/CDNHTTP cacheResponse publik
AplikasiSymfony Cache (PSR-6/16)Query result, konfigurasi, render parsial
DatabaseQuery cache bawaan DBHasil query SQL

Episode ini fokus pada dua yang dipegang langsung aplikasi: Cache component (di dalam PHP) dan HTTP cache (via header).

Cache Pools: PSR-6 dan PSR-16

Install component cache:

Install Cache component
composer require symfony/cache

Symfony mengikuti dua standar:

  • PSR-6CacheItemPoolInterface: API dengan item object (getItem(), save()).
  • PSR-16CacheInterface / SimpleCache: API singkat (get(), set()).

Contoh memakai SimpleCache (paling sering dipakai):

Cache hasil query
use Psr\SimpleCache\CacheInterface;
 
public function articleCount(CacheInterface $cache): int
{
    $count = $cache->get('article_count');
    if ($count === null) {
        $count = $this->articleRepository->count([]);
        $cache->set('article_count', $count, 3600);
    }
 
    return $count;
}

'article_count' disimpan 3600 detik, dan dipakai ulang tanpa menyentuh database. Pool default cache.app tersedia; pool lain dibuat di konfigurasi.

Memilih Adapter: APCu vs Redis

Pool diidentifikasi dengan adapter — backend penyimpanan:

config/packages/cache.yaml
framework:
    cache:
        pools:
            app.result_pool:
                adapter: cache.adapter.redis
                default_lifetime: 600
 
            app.fast_pool:
                adapter: cache.adapter.apcu
                default_lifetime: 60
AdapterKecepatanPersistensiCocok untuk
cache.adapter.filesystemSedangDiskDev, single node
cache.adapter.apcuSangat cepatPer-prosesData transien, per-server
cache.adapter.redisCepatTerpusatMulti-server, shared cache

Aturan praktis: APCu untuk data yang boleh basi per-server (konfigurasi, metadata); Redis untuk data yang harus konsisten lintas server (session, rate limit, shared result). Redis akan berperan besar saat scaling di episode 24.

HTTP Cache: Cache-Control

Cache paling murah adalah yang tidak pernah sampai ke aplikasi — didelegasikan ke proxy/browser via header. Set cache header pada response:

Cache header pada response
public function index(): Response
{
    $response = $this->render('article/index.html.twig', [
        'articles' => $this->articleRepository->findLatest(10),
    ]);
 
    $response->setPublic();
    $response->setMaxAge(60);          // 60 detik di proxy
    $response->setSharedMaxAge(60);    // khusus untuk shared cache
 
    return $response;
}

Hasilnya: proxy/CDN menyimpan halaman 60 detik, permintaan kedua tidak menyentuh PHP sama sekali. Di development, kalian bisa melihat status cache dengan header respons.

Invalidasi HTTP cache tidak se-trivial cache aplikasi: karena proxy tidak tahu perubahan database, gunakan cache tags (untuk reverse proxy yang mendukungnya) atau kurangi maxAge — atau pakai pendekatan ESI untuk fragment cache.

ESI: Cache Parsial

Banyak halaman punya bagian yang berubah cepat (keranjang, counter) di tengah halaman yang lambat berubah. Edge Side Includes (ESI) memungkinkan proxy menyimpan bagian halaman:

Aktifkan ESI di framework.yaml
framework:
    esi: true
    fragments: { path: /_fragment }
Fragment ESI di Twig
<div id="cart">
    {{ render_esi(controller('App\\Controller\\CartController::summary')) }}
</div>

render_esi() meminta proxy merender fragment secara terpisah — halaman induk di-cache lama, fragment cart di-cache singkat. Ini strategi klasik untuk portal dan e-commerce.

OPCache

Jangan lupa lapisan paling dasar: OPCache meng-compile PHP bytecode dan menyimpannya di memory, menghemat kompilasi ulang setiap request. Ini wajib aktif di production:

php.ini — rekomendasi opcache
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0

validate_timestamps=0 di production membuat opcache tidak mengecek file setiap request (perlu cache:clear/restart saat deploy). Tanpa opcache, semua optimasi lain terasa sia-sia — ini optimasi dengan rasio usaha/hasil terbaik.

Strategi Invalidasi yang Benar

Ini bagian yang paling sering salah. Tiga strategi inti:

  1. TTL sederhana — terima data basi hingga N detik; cocok untuk data yang toleran.
  2. Invalidasi saat menulis — hapus/re-generate cache tepat ketika data diubah.
  3. Cache tags — tandai cache dengan kelompok; hapus semua tag saat kelompok berubah.

Contoh strategi 2 dengan cache:pool:delete atau memakai cache dan hapus setelah flush:

Invalidasi setelah menulis
use Psr\SimpleCache\CacheInterface;
 
public function save(Article $article, CacheInterface $cache): void
{
    $this->em->persist($article);
    $this->em->flush();
 
    $cache->delete('article_count');
}

Warning

Cache tags tersedia di pool berbasis Redis (cache.adapter.redis_tag_aware) dan didukung taggable cache. Jangan berasumsi semua adapter mendukung tags — pool filesystem & APCu tidak. Pastikan pilihan adapter kalian sesuai sebelum mengandalkan tags.

Menelusuri Cache

Beberapa perintah yang membantu operasional:

Kelola cache pools
php bin/console cache:pool:list          # daftar pool & ukuran
php bin/console cache:pool:clear         # kosongkan semua pool
php bin/console cache:pool:delete cache.app article_count
php bin/console cache:clear              # bersihkan cache framework

Ingat: cache:clear membersihkan cache framework (route, container, twig), sedangkan cache:pool:clear membersihkan cache aplikasi (data yang kalian simpan). Keduanya sering tertukar saat debugging.

Penutup

Pada episode 14 ini, kalian telah mempercepat aplikasi dengan cache berlapis.

Inti yang harus dibawa pulang:

  • Cache berlapis: browser → proxy → aplikasi → database; kelola yang di tangan aplikasi.
  • PSR-6 (CacheItemPoolInterface) dan PSR-16 (SimpleCache) — pakai SimpleCache untuk umum.
  • Adapter: APCu per-server, Redis untuk konsistensi lintas server.
  • HTTP cache via Cache-Control; ESI untuk fragment; opcache wajib di produksi.
  • Invalidasi lebih penting dari penyimpanan: TTL, invalidate-on-write, atau cache tags.

Di episode 15 selanjutnya kita jadi detektif: Logging, Profiler & Debugging — Monolog channel & structured logging, Symfony Profiler & Web Debug Toolbar, var-dumper, dan teknik men-debug performa & error di production. Sampai jumpa di episode 15!

Belajar Symfony - Caching & Performance | Belajar Symfony