Mengelola session dan state di mode worker: mengapa session file kurang ideal, memindahkan session ke Redis dengan handler Redis, menjaga aplikasi stateless agar worker tetap sehat, dan memahami batasan enable_post_data_reading serta request_body_timeout.

Setelah episode 12 membangun observability, episode ini menangani salah satu kejutan paling umum bagi developer PHP yang baru pindah ke app server: session dan state. Di PHP-FPM, session disimpan di file per-request dan setiap proses punya lifecycle pendek, jadi kebocoran state jarang terasa. Di mode worker, aplikasi hidup terus — dan desain state yang ceroboh langsung terlihat.
Mengapa penting? Dua masalah menanti di mode worker: (1) session berbasis file tidak berbagi data antar instance dan bisa macet karena lock, dan (2) state aplikasi yang bocor antar request adalah sumber bug paling halus. Episode ini memberi kalian pola yang benar untuk keduanya.
Worker mode menuntut aplikasi stateless — data yang bersifat per-request tidak boleh disimpan di dalam proses. Ingat daftar dari episode 5: variabel static, static property, dan variabel global bertahan antar request. Setiap data yang berkaitan dengan satu pengguna atau satu request harus hidup di luar proses worker:
| Data | Tempat yang benar |
|---|---|
| Login pengguna | Session (Redis) / token |
| Keranjang belanja | Session / database |
| Cache berumur pendek | Redis / database |
| Konfigurasi | Env var (dibaca saat start) |
Aturan praktisnya: jika datanya milik satu pengguna, ia tidak boleh tinggal di memori worker.
Default PHP menyimpan session sebagai file (session.save_handler = files). Di mode worker (atau multi-instance), ini bermasalah:
session.gc berjalan tidak menentu.Pindahkan session handler ke Redis — storage eksternal yang dibagikan antar instance dan bebas lock. Install ekstensi redis/igbinary dulu (episode 21 membahas build ekstensi):
{
frankenphp {
php_ini session.save_handler redis
php_ini session.save_path "tcp://redis:6379"
php_ini session.gc_maxlifetime 1440
}
}
localhost {
root * public/
php_server
}Atau setel di php.ini:
session.save_handler = redis
session.save_path = "tcp://redis:6379?timeout=2"
session.gc_maxlifetime = 1440Verifikasi dari PHP:
<?php
var_dump(ini_get('session.save_handler')); // "redis"Tip
Redis bukan satu-satunya opsi. Handler lain yang eksternal dan dibagikan (memcached, database) juga memenuhi syarat. Kuncinya bukan teknologi, melainkan storage di luar proses worker — itu yang membuat session aman dari restart worker dan bisa dibagi antar instance.
Selain session, cache aplikasi (Laravel CACHE_STORE=redis, Symfony cache.adapter.redis) sebaiknya diarahkan ke Redis:
php artisan config:set cache.store redis
php artisan config:cacheKombinasi session + cache di Redis membuat worker benar-benar bisa dianggap "tanpa memori" — setiap request membaca state dari sumber eksternal yang konsisten.
enable_post_data_reading dan request_body_timeoutBeberapa aplikasi butuh membaca body request di luar alur normal PHP (misal untuk signature validation atau raw body parsing). Direktif enable_post_data_reading mengontrol apakah PHP membaca data POST otomatis. Di FrankenPHP, kontrol utama adalah request_body_timeout — batas waktu idle untuk membaca body:
localhost {
root * public/
php_server {
request_body_timeout 60s
env ENABLE_POST_DATA_READING 0
}
}request_body_timeout 60s (default) — klien yang berhenti mengirim (slow POST) diputus; upload yang stabil mengalir lancar.0 menonaktifkan batas — hati-hati dengan risiko slow-loris.ENABLE_POST_DATA_READING=0 agar PHP tidak menelan php://input sebelum kalian membacanya.Tidak semua state itu musuh. Di worker mode, cache dalam memori yang tidak bergantung request adalah anugerah performa — misalnya metadata konfigurasi yang dibaca sekali saat start, atau koneksi database yang disimpan dalam variabel worker. Yang perlu dihindari adalah state yang menyimpan data per-request secara diam-diam:
<?php
// SALAH: data per-request bocor ke request lain
class AuthHolder {
public static ?string $userId = null;
}
// BENAR: konfigurasi read-only, dibaca sekali
class ConfigHolder {
public static array $settings = [];
}
// (diisi saat bootstrap, tidak pernah diubah per-request)Framework modern (Laravel Octane, Symfony) menangani reset service otomatis — gunakan itu daripada menulis ulang mekanisme sendiri.
Danger
Pola "singleton service yang menyimpan current user" adalah bug worker paling umum di aplikasi PHP. Framework seperti Symfony memecahkannya lewat ResetInterface (episode 7) dan Laravel via Octane state reset — jangan berinovasi di area ini jika tidak perlu.
session.save_handler dulu.$_ENV di-tulis saat request (episode 5): kebocoran antar request, sulit dilacak.Pada episode 13 ini, kalian telah mengelola session dan state di mode worker.
Inti yang harus dibawa pulang:
request_body_timeout melindungi dari slow POST; ENABLE_POST_DATA_READING=0 untuk raw body.Di episode 14 selanjutnya kita memasuki fase keamanan: Security — TLS, Headers & OWASP — memastikan HTTPS only, memasang security headers (CSP, HSTS, X-Frame-Options), dan mengeraskan worker dari eksposur yang tidak perlu. Sampai jumpa di episode 14!