Sepanjang section ini kita membangun ingatan di atas HTTP: cookie menyimpan data kecil di browser, session menyimpan data besar di server. Semua itu berjalan mulus selama aplikasi berjalan di satu server. Kenyataannya, aplikasi yang sukses akan tumbuh melewati kapasitas satu mesin — dan di situlah desain state (stateless atau stateful) menentukan apakah penambahan server jadi perkara sehari-hari atau mimpi buruk. Chapter penutup section ini membedah kedua gaya arsitektur tersebut, cara PHP berperilaku di keduanya, dan cara memilih pendekatan yang tepat untuk proyek Anda.

Masalah Ketika Server Ditambah #

Naik skala ada dua arah. Vertical scaling menambah kapasitas satu mesin (RAM, CPU lebih besar) — sederhana, tapi ada langit-langit harga dan satu titik kegagalan. Horizontal scaling menambah jumlah mesin di belakang load balancer — lebih murah per unit dan tahan gangguan, tetapi membawa satu pertanyaan genting: ketika request pengguna boleh mendarat di server mana pun, di mana ingatannya disimpan?

Bayangkan dua server PHP yang sama-sama memakai penyimpanan session bawaan: file sess_<id> di direktori masing-masing. Pengguna login di server A, lalu request berikutnya diarahkan load balancer ke server B. Server B mencari file session tersebut di direktorinya sendiri, tidak menemukannya, dan memperlakukan pengguna sebagai tamu — padahal login baru terjadi dua detik lalu. Semua mekanisme section ini (keranjang belanja, status login, flash message) runtuh seketika. Dua jalan keluar menyambut kita: membuat state tetap di server tetapi dipusatkan (stateful), atau membuang state dari server sama sekali (stateless).

Aplikasi Stateful dengan Session Terpusat #

Gaya stateful mempertahankan session di sisi server, tetapi memindahkan penyimpanannya ke tempat yang bisa dilihat semua server. Dua teknik yang umum:

  1. Sticky session — load balancer mengunci satu pengguna ke satu server (biasanya lewat cookie penanda), sehingga file session lokal selalu berada di tempat yang sama. Sederhana, tetapi menggagalkan tujuan penyeimbangan beban: satu server bisa kebanjiran sementara yang lain nganggur, dan server yang mati membawa serta seluruh session yang dikuncikan padanya.
  2. Session storage bersama — semua server membaca dan menulis ke satu penyimpanan eksternal. Dengan PHP-FPM, pilihan populer adalah Redis atau Memcached karena keduanya menyimpan data di memori dengan latensi sangat rendah.

Diagram berikut memperlihatkan konfigurasi kedua: load balancer membagi request ke tiga server PHP, dan seluruh server menunjuk ke satu penyimpanan session yang sama.

Dengan penyimpanan bersama, server mana pun yang menerima request akan menemukan session yang sama — masalah tadi hilang. Pada PHP, perpindahan ini tidak mengubah kode aplikasi sama sekali: cukup ganti dua direktif php.ini, karena antarmuka $_SESSION tetap, yang berubah hanya handler penyimpanan di baliknya:

ini
; php.ini produksi: simpan session di Redis bersama
session.save_handler = redis
session.save_path = "tcp://10.0.0.5:6379?auth=YOUR_REDIS_PASSWORD"

Sekarang penyimpanan bersama itu sendiri menjadi komponen kritis yang harus tersedia tinggi dan di-backup — kita memindahkan titik kegagalan, bukan menghapusnya. Redis dan Memcached pada akhirnya juga menyimpan data di memori sehingga perlu direplikasi bila session tidak boleh hilang.

Aplikasi Stateless dengan State di Setiap Request #

Gaya stateless mengambil jalan sebaliknya: server tidak menyimpan apa pun tentang pengguna. Seluruh keadaan yang diperlukan dibawa di dalam setiap request — biasanya sebagai token di header Authorization — sehingga request mana pun bisa dilayani server mana pun, tanpa koordinasi. Konsep paling terkenal di jalur ini adalah JSON Web Token (JWT): token yang memuat data (payload) dan tanda tangan kriptografis sehingga penerima dapat memverifikasi keasliannya tanpa bertanya ke penyimpanan mana pun.

Mari kita buat versi sederhanannya untuk merasakan idenya — payload base64 plus tanda tangan HMAC, dipisah titik:

php
<?php
function buatTokenStateless(array $data, string $secret): string
{
    $payload = base64_encode(json_encode($data));
    $signature = hash_hmac('sha256', $payload, $secret);
    return $payload . '.' . $signature;
}

function bacaTokenStateless(string $token, string $secret): ?array
{
    [$payload, $signature] = array_pad(explode('.', $token, 2), 2, '');
    if (!hash_equals(hash_hmac('sha256', $payload, $secret), $signature)) {
        return null; // tanda tangan tidak sah
    }
    return json_decode(base64_decode($payload), true);
}

$secret = 'kunci-rahasia-aplikasi';

// Server menerbitkan token; seluruh identitas dibawa di dalam token
$token = buatTokenStateless(['user_id' => 42, 'role' => 'editor'], $secret);
echo 'Token sah: ' . json_encode(bacaTokenStateless($token, $secret)) . "\n";

// Penyerang mengubah payload agar role menjadi admin
[$payload, $signature] = explode('.', $token, 2);
$tokenPalsu = base64_encode(json_encode(['user_id' => 42, 'role' => 'admin'])) . '.' . $signature;
echo 'Payload diganti: ' . var_export(bacaTokenStateless($tokenPalsu, $secret) === null, true) . "\n";
echo 'Kunci salah: ' . var_export(bacaTokenStateless($token, 'kunci-yang-salah') === null, true) . "\n";

Output yang Dihasilkan:

text
Token sah: {"user_id":42,"role":"editor"}
Payload diganti: true
Kunci salah: true

Perhatikan yang tidak ada dalam kode di atas: tidak ada session_start(), tidak ada penyimpanan, tidak ada pencarian. Verifikasi murni berhitung — hash_hmac() atas payload lalu dibandingkan dengan hash_equals(). Output membuktikan sifat pentingnya: payload yang diubah ditolak meski tanda tangan lama dibawa ulang, karena tanda tangan terikat pada isi payload persisnya. JWT sungguhan menambahkan header dan kedaluwarsa di dalam payload, tetapi prinsipnya sama persis — kita akan membedahnya tuntas di section REST API Development.

Konsekuensi praktisnya ada dua sisi. Kelebihan: server menjadi saling kebal (state di browser/server mana saja bisa melayani), dan menambah server tidak menyentuh logika aplikasi. Kekurangan: token tidak bisa “dicabut” sesuka session — sampai kedaluwarsa, token yang sah tetap sah — dan ukurannya membesar karena setiap request membawa identitas. Justru karena itu token stateless umumnya diberi umur pendek (15 menit) dan dipasangkan mekanisme penyegaran.

Shared-Nothing, Warisan Alami PHP #

PHP lahir dengan arsitektur shared-nothing: setiap request dijalankan oleh proses yang mulai dari keadaan kosong, variabel global lenyap di akhir request, dan tidak ada memori bersama antar-request. Perilaku ini bisa kita rasakan langsung — bahkan dua koneksi database sqlite::memory: dalam satu skrip pun tidak saling mengenal, karena masing-masing mendapat database privatnya sendiri:

php
<?php
// Koneksi A: membuat tabel di memori privat miliknya sendiri
$pdoA = new PDO('sqlite::memory:');
$pdoA->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdoA->exec('CREATE TABLE hitungan (n INTEGER)');
$pdoA->exec('INSERT INTO hitungan (n) VALUES (42)');

echo 'Koneksi A membaca hitungan: ' . $pdoA->query('SELECT n FROM hitungan')->fetchColumn() . "\n";

// Koneksi B: database memori yang benar-benar terpisah
$pdoB = new PDO('sqlite::memory:');
$pdoB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $pdoB->query('SELECT n FROM hitungan');
    echo "Koneksi B melihat tabel yang sama\n";
} catch (PDOException $e) {
    echo 'Koneksi B tidak melihat tabel: ' . $e->getMessage() . "\n";
}

Output yang Dihasilkan:

text
Koneksi A membaca hitungan: 42
Koneksi B tidak melihat tabel: SQLSTATE[HY000]: General error: 1 no such table: hitungan

Koneksi B menolak query dengan galat no such table karena tabel hitungan hanya hidup di memori koneksi A. Begitu pula proses PHP antar-request: satu proses tidak pernah melihat variabel proses lain. Ini berarti PHP secara alami condong ke desain stateless — server stateless PHP tidak membutuhkan perubahan apa pun. Session adalah satu-satunya bagian yang “melanggar” kepribadian ini, dan itulah mengapa seluruh section ini membahas session dengan begitu banyak perhatian.

Perbandingan dan Panduan Memilih #

Kedua gaya bukan pesaing yang saling menggantikan, melainkan alat dengan biaya berbeda. Tabel berikut merangkum pertukarannya:

AspekStateful (session di server)Stateless (token di request)
Tempat statepenyimpanan server (file, Redis)dibawa client di setiap request
Menambah serverbutuh sticky session atau storage bersamalangsung, tanpa koordinasi
Pencabutan aksesseketika (hapus session)tertunda sampai token kedaluwarsa
Ukuran per requestkecil (hanya cookie ID)membesar mengikuti isi token
Beban servermemori/penyimpanan session aktifhanya komputasi verifikasi
Cocok untukaplikasi web dengan browserAPI publik, microservice, mobile

Panduan praktisnya: aplikasi web klasik yang dilayani browser paling sederhana memakai session — dan begitu perlu beberapa server, pasang Redis sebagai penyimpanan bersama. API yang dikonsumsi aplikasi pihak ketiga atau mobile lebih alami memakai token stateless. Banyak sistem nyata memakai keduanya sekaligus: session untuk pengalaman browser, token untuk akses API, seperti yang akan kita praktikkan di section Authentication dan REST API Development.

Ringkasan #

Stateful menaruh ingatan di server — aman dicabut, tetapi menuntut penyimpanan bersama seperti Redis saat server berjumlah banyak. Stateless membawa ingatan di setiap request melalui token bertanda tangan — mudah diskalakan, tetapi sulit dicabut dan lebih gemuk. Arsitektur shared-nothing PHP membuat keduanya mudah dijangkau: $_SESSION cukup diganti handler-nya untuk menjadi terpusat, sementara request PHP yang selalu mulai dari nol adalah fondasi stateless yang alami. Dengan seluruh bekal HTTP state ini, kita siap memasuki section berikutnya: menyimpan data yang lebih awet daripada login — database relasional dan SQL.

Referensi #