Belajar RoadRunner - Worker Pool Tuning & Performance
Episode 20 of 26

Belajar RoadRunner - Worker Pool Tuning & Performance

Mengoptimalkan performa RoadRunner: menyetel num_workers, max_jobs, max_worker_memory, dan supervisor, memanfaatkan opcache dan JIT, melakukan benchmark sistematis, serta membandingkan hasil dengan PHP-FPM dan FrankenPHP.

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

Pendahuluan

Setelah di episode 19 kita mengeksplorasi Temporal, pada episode kali ini kita fokus ke hal yang selalu ditanya saat adopsi RoadRunner: berapa cepat? Jawabannya tergantung tuning — worker pool yang disetel asal-asalan justru lebih lambat dari PHP-FPM. Episode ini adalah panduan menyetel pool, opcache, dan JIT secara sistematis.

Mengapa episode ini penting? Karena performa bukan satu tombol — ia hasil dari sekumpulan parameter yang saling memengaruhi. Memahami hubungan antar parameter membuat kalian bisa mendiagnosis bottleneck, bukan sekadar menebak angka.

Parameter Pool dan Hubungannya

Config pool yang disetel
http:
  address: 0.0.0.0:8080
  pool:
    num_workers: 8
    max_worker_memory: 128
    max_jobs: 1000
    allocate_workers_by_tasks: false
    supervisor:
      max_worker_memory: 128
      exec_ttl: 30
      idle_ttl: 60
ParameterEfek performaCara menyetel
num_workersSeberapa banyak request diproses paralelMulai dari core CPU; naikkan sampai ada gain nyata
max_jobsFrekuensi restart worker (memori segar vs overhead restart)Lebih kecil untuk aplikasi rawan leak; besar untuk stabil
max_worker_memoryKapan worker direstart karena bocorPantau baseline memory di metric
allocate_workers_by_tasksSpawn bertahap (hemat idle) atau penuh (siap beban)true untuk traffic naik-turun; false bila butuh respons instan
idle_ttlRecycle worker idleKecil di dev (hemat resource), besar di prod

Aturan penting: num_workers tidak otomatis = jumlah core. Worker PHP sering blocking (menunggu DB/API) — lebih banyak worker menyerap latensi tersebut, tapi menambah memory. Di sinilah benchmark berperan.

Opcache dan JIT

Worker persistent memberi kesempatan emas: bootstrap sekali, bytecode hangat. Aktifkan opcache di CLI worker:

php.ini - konfigurasi opcache untuk worker
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.validate_timestamps=0
opcache.jit=tracing
opcache.jit_buffer_size=64M
PengaturanEfek
opcache.enable_cli=1Bytecode PHP di-cache di worker CLI — wajib
opcache.validate_timestamps=0Jangan cek ulang mtime tiap request (dev: 1)
opcache.jit=tracingAktifkan JIT tracing (PHP 8+) untuk loop/CPU-bound
opcache.jit_buffer_size=64MBuffer JIT cukup besar

Tip

Karena worker persistent, manfaat opcache/JIT terasa jauh lebih besar daripada di FPM: file di-compile sekali di awal worker, lalu semua request memakai bytecode hangat. JIT memberi gain signifikan pada kode CPU-bound (encryption, serialization).

Benchmark Sistematis

Jangan benchmark dengan tebakan. Pakai tool beban HTTP seperti hey atau wrk:

Benchmark 30 detik, 100 concurrency
./rr serve -c .rr.yaml &
hey -n 30000 -c 100 http://localhost:8080/api/health

Langkah metodis:

  1. Baseline: jalankan tanpa tuning (config default), catat RPS dan latensi p95.
  2. Satu variabel per percobaan: ubah num_workers (4 → 8 → 16 → 32), catat hasil.
  3. Cari plateau: jika menambah worker tidak menaikkan RPS lagi, itu batas — kemungkinan CPU/DB, bukan worker.
  4. Uji lama: jalankan 5+ menit, amati worker_restarts_total dan memory (episode 12).

Contoh template tabel hasil:

num_workersRPSp95 (ms)Memory total (MB)
4120045210
8210028410
16240024820
322400241600

Pada contoh ini, 16 adalah titik optimal — 32 hanya membuang memory.

RoadRunner vs PHP-FPM vs FrankenPHP

Dengan data benchmark yang sama (satu worker, aplikasi sama, tanpa cache eksternal), pola umumnya:

AspekPHP-FPMRoadRunnerFrankenPHP
ModelPer-request bootstrapWorker pool persistentThread persistent
Bootstrap sekaliTidakYaYa
RPS (dengan framework berat)Baseline (1x)2-4x lebih tinggi2-4x lebih tinggi
Memory footprintKecil per requestSebanding jumlah workerSebanding jumlah thread
Fleksibilitas pluginTidakSangat luasTerbatas

Pola yang paling terasa: framework berat (Laravel/Symfony) mendapat gain terbesar karena bootstrap dihilangkan. Untuk skrip ringan tanpa framework, selisihnya kecil.

Warning

Angka benchmark antar-app server hanya bermakna bila metodologinya sama. Banyak artikel membandingkan "hello world" tanpa framework — hasilnya menyesatkan untuk keputusan production. Benchmark dengan aplikasi kalian sendiri, di mesin yang sama.

Diagnosa Bottleneck Umum

GejalaKemungkinan penyebabAksi
RPS tidak naik walau worker ditambahCPU/core habis, atau DB menjadi bottleneckProfil CPU; cek query DB
Latensi p95 tinggi tapi p50 rendahBeberapa worker terganggu (GC/restart)Perbesar max_jobs, cek restart
Memory pool membengkakWorker bocorKecilkan max_worker_memory, audit kode
Satu worker melayani lambat semuaShared resource (DB connection pool)Ukur di sisi DB

Penutup

Pada episode 20 ini, kalian telah belajar tuning performa.

Inti yang harus dibawa pulang:

  • Setiap parameter pool saling terkait — ubah satu per satu.
  • opcache.enable_cli=1 + JIT: wajib untuk worker persistent.
  • Benchmark sistematis dengan hey/wrk, cari plateau.
  • RoadRunner unggul 2-4x di framework berat vs PHP-FPM.
  • Diagnosa bottleneck dengan metric, bukan menebak.

Di episode 21 selanjutnya, kita belajar Docker & Kubernetes deployment — membangun image multi-stage PHP + binary RoadRunner, menyusun compose dan healthcheck, lalu deploy ke Kubernetes dengan HPA dan graceful shutdown SIGTERM. Sampai jumpa di episode 21!

Belajar RoadRunner - Worker Pool Tuning & Performance | Belajar RoadRunner