Sesi 1: Satu User, Satu Server
1. Bayangkan jika
Bayangkan jika hanya ada satu orang di seluruh Indonesia yang memakai aplikasi ini.
Namanya Pak Andi. Jam sembilan pagi, dia duduk di warung kopi, membuka aplikasi di ponselnya, dan menekan tombol Cek Status. Tidak ada pengguna lain. Tidak ada antrean. Server sepenuhnya menganggur — CPU 2%, memori lega, database kosong dari beban.
Kondisi paling ideal yang pernah bisa dialami sebuah sistem.
Layar berputar sebentar. Statusnya muncul. Butuh 1,4 detik.
Pertanyaannya: 1,4 detik itu dihabiskan untuk apa?
Tidak ada yang antre. Tidak ada yang berebut. Tidak ada satu pun alasan "sistem sedang sibuk". Dan tetap saja, ada 1.400 milidetik yang harus dipertanggungjawabkan.
Sesi ini adalah perjalanan mengikuti satu request itu — seperti mengikuti satu tetes air dari keran sampai ke laut, lalu kembali lagi. Karena kalau kita tidak paham ke mana waktu pergi saat sistem kosong, kita tidak akan pernah paham apa yang rusak saat sistem penuh.
2. Apa yang sebenarnya terjadi
Ketika jari Pak Andi menyentuh layar, request-nya melewati sekitar sepuluh tahap. Sebagian besar tahap itu bukan "aplikasi kita".
Tahap 1 — Mencari alamat (DNS lookup). Ponsel Pak Andi tahu nama domain server, tapi tidak tahu alamat IP-nya. Dia harus bertanya dulu ke DNS resolver. Kalau jawabannya sudah tersimpan di cache ponsel: nol milidetik. Kalau belum: 20–100ms hanya untuk bertanya "di mana rumahnya?".
Tahap 2 — Mengetuk pintu (TCP handshake). Sebelum satu byte data pun dikirim, ponsel dan server harus saling menyapa tiga kali: "halo" → "halo juga" → "oke". Tiga perjalanan bolak-balik ini memakan waktu satu round trip penuh. Di jaringan 4G Indonesia, satu round trip realistisnya 40–80ms.
Tahap 3 — Bertukar kunci (TLS handshake). Ini data pribadi, jadi semuanya harus terenkripsi. TLS handshake butuh satu sampai dua round trip lagi, ditambah kerja kriptografi di kedua sisi. Tambahkan 80–150ms.
Perhatikan: kita sudah menghabiskan sekitar 200ms, dan aplikasi kita belum melihat satu huruf pun dari permintaan Pak Andi.
Tahap 4 — Permintaan berangkat. Request HTTP dikirim melintasi jaringan seluler, masuk ke jaringan operator, keluar ke internet, masuk ke jaringan data center. 40–80ms.
Tahap 5 — Pintu depan menerima (web server / reverse proxy). Sebuah proses di depan aplikasi menerima koneksi itu, membuka enkripsinya, membaca header, memutuskan ke mana request ini harus dilempar. Cepat — 1–5ms — tapi tahap ini penting untuk diingat, karena di Sesi 10 kita akan melihat bagaimana tahap yang cepat ini bisa menjadi penyebab kematian sistem.
Tahap 6 — Sebuah thread mengangkat pekerjaan. Aplikasi punya sejumlah pekerja — sebut saja thread — dan salah satunya mengambil request ini. Saat sistem kosong, selalu ada pekerja yang menganggur, jadi tidak ada waktu tunggu. Ingat baik-baik kalimat itu. Kalimat itulah yang akan berubah total di Sesi 3.
Tahap 7 — Aplikasi berpikir. Validasi token, cek otorisasi, susun query. Ini "kode kita". Realistisnya 5–20ms.
Tahap 8 — Bertanya ke database. Aplikasi meminjam satu koneksi dari connection pool, mengirim query, database membaca dari memori atau disk, dan mengembalikan barisnya. Query yang sederhana dan ter-index dengan baik: 2–15ms. Ditambah perjalanan jaringan di dalam data center: 1–2ms.
Tahap 9 — Menyusun jawaban. Hasil query diubah jadi JSON, dienkripsi kembali, dikirim keluar. Beberapa milidetik.
Tahap 10 — Pulang. Melintasi jaringan yang sama, kembali ke ponsel. 40–80ms. Lalu ponsel harus menggambar layarnya — 50–200ms tergantung seberapa tua ponselnya dan seberapa berat halamannya.
Anggaran waktunya
| Tahap | Perkiraan | Milik siapa? |
|---|---|---|
| DNS lookup | 0–150 ms | Jaringan / operator |
| TCP handshake | 40–120 ms | Jaringan |
| TLS handshake | 80–260 ms | Jaringan + kripto |
| Request berangkat | 40–120 ms | Jaringan |
| Web server / proxy | 1–5 ms | Infrastruktur kita |
| Aplikasi berpikir | 5–20 ms | Kode kita |
| Query database | 3–17 ms | Data kita |
| Menyusun respons | 2–5 ms | Kode kita |
| Respons pulang | 40–320 ms | Jaringan |
| Ponsel menggambar | 50–400 ms | Perangkat user |
Rentang bawah adalah hari yang baik: sinyal kuat, koneksi sudah hangat, ponsel baru. Rentang atas adalah pagi hari di warung kopi dengan sinyal seadanya — dan di situlah 1,4 detik yang dirasakan Pak Andi berada.
Jumlahkan kolom "kode kita" dan "data kita": sekitar 10 sampai 42 milidetik.
Dari 1,4 detik yang dirasakan Pak Andi, bagian yang benar-benar kita kendalikan mungkin hanya 2%.
3. Bagaimana jika kita coba...
Bagaimana jika kita optimasi query-nya?
Katakanlah kita bekerja keras dan berhasil memangkas query dari 15ms jadi 3ms. Hemat 12ms. Pak Andi sekarang menunggu 1,388 detik, bukan 1,4 detik.
Dia tidak akan merasakan apa pun.
Bagaimana jika kita naikkan CPU server-nya dua kali lipat?
Tahap 7 dan 9 mungkin jadi setengah lebih cepat. Hemat sekitar 10ms. Pak Andi masih menunggu 1,39 detik. Kita baru saja menggandakan biaya server untuk perbaikan yang tidak terlihat oleh siapa pun.
Ini pelajaran pertama seri ini, dan salah satu yang paling mahal kalau dilupakan:
Mengoptimasi bagian yang bukan penyumbang terbesar tidak menghasilkan apa-apa — sehebat apa pun optimasinya.
Kalau sebuah sistem lambat saat sepi, jawabannya hampir tidak pernah ada di dalam aplikasi. Dia ada di jumlah round trip, di ukuran payload, di handshake yang diulang-ulang, atau di ponsel user itu sendiri.
Yang benar-benar berpengaruh di skenario Pak Andi justru hal-hal yang terdengar tidak seksi: menggunakan ulang koneksi yang sudah terbuka supaya handshake tidak diulang, mengecilkan payload supaya lebih sedikit paket yang dikirim, dan mendekatkan titik masuk ke user supaya round trip-nya lebih pendek.
Tapi — dan ini kuncinya — cerita ini berubah total begitu Pak Andi tidak sendirian lagi. Saat ada 5.000 orang bersamaan, 15ms query itu bisa berubah menjadi 4 detik tanpa satu baris kode pun berubah. Kenapa itu bisa terjadi adalah isi tiga sesi berikutnya.
4. Nama resminya
Yang barusan kita telusuri punya nama-nama yang akan muncul di dokumen desain:
Request lifecycle — perjalanan lengkap satu permintaan dari klien ke sistem dan kembali. Kalau ada yang bertanya "di mana bottleneck-nya", pertanyaan sesungguhnya adalah: "di tahap mana dari lifecycle ini?"
Round trip time (RTT) — waktu satu perjalanan pulang-pergi antara klien dan server. Angka ini adalah lantai. Sistem tidak bisa lebih cepat dari jumlah RTT yang dibutuhkannya, sehebat apa pun servernya.
Handshake — proses perkenalan sebelum data bisa mengalir. TCP handshake untuk membuka koneksi, TLS handshake untuk mengamankannya.
Keep-alive / connection reuse — menjaga koneksi tetap terbuka supaya request berikutnya melewatkan Tahap 2 dan 3 sepenuhnya. Di dokumen desain ini muncul sebagai atribut kecil seperti keepalive_timeout — dan sering menjadi selisih ratusan milidetik.
Thread — satu unit pekerja di aplikasi yang menangani satu request pada satu waktu. Jumlahnya terbatas. Sekarang ini terasa tidak penting; di Sesi 5 ini akan menjadi salah satu penyebab kematian sistem yang paling sering.
Connection pool — sekumpulan koneksi database yang sudah terbuka dan dipakai bergantian, karena membuka koneksi database itu mahal. Di Sesi 13, dua kata ini akan menjadi pusat dari seluruh cerita.
TLS termination — titik di mana enkripsi dibuka. Di sesi ini terjadi di web server. Di Sesi 9 kita akan lihat kenapa memindahkan titik ini mengubah banyak hal.
5. Di dunia kita
Diagram di atas — satu klien, satu server, satu database — adalah versi yang paling disederhanakan. Di sistem produksi sungguhan, Tahap 8 jarang sesederhana itu.
Ketika Pak Andi mengecek status pesanannya, aplikasi tidak selalu membaca dari database miliknya sendiri. Sering kali dia harus bertanya ke layanan lain: lewat API gateway internal, lalu ke sistem inventori, mungkin melewati satu atau dua lapis lagi. Setiap lompatan itu adalah round trip baru — dengan handshake-nya sendiri, timeout-nya sendiri, dan kemungkinan gagalnya sendiri.
Artinya "satu request user" di sistem yang sudah dipecah jadi banyak layanan sebenarnya bisa berarti empat sampai enam request internal yang berantai. Kalau tiap lompatan menambah 20ms, empat lompatan menambah 80ms — sebelum ada satu pun pekerjaan berguna dilakukan.
Konsekuensi yang perlu dipegang sejak sekarang: di sistem berlapis, latency itu menumpuk, dan kegagalan itu menular. Kedua hal itu adalah tema Level 3.
6. Lensa performance tester
Kalau kamu menguji sistem, sesi ini punya empat implikasi langsung:
Selalu ukur baseline satu user dulu. Sebelum menjalankan skenario 5.000 virtual user, jalankan satu user saat sistem kosong. Angka itu adalah lantai — sistem tidak akan pernah lebih cepat dari itu di bawah beban. Kalau baseline-nya sudah 1,4 detik, tidak ada gunanya menargetkan 800ms di beban puncak. Dan kalau angka baseline naik dari sprint ke sprint, itu regresi murni yang tidak tertutupi oleh apa pun.
Ketahui persis di mana load generator kamu berdiri. Kalau generator berada di dalam data center, kamu memotong hampir seluruh biaya jaringan — Tahap 1, 2, 3, 4, dan 10. Hasilnya akan jauh lebih bagus dari kenyataan yang dialami pengguna. Itu bukan tes yang salah, tapi hasilnya menjawab pertanyaan yang berbeda. Pastikan semua orang tahu pertanyaan mana yang sedang dijawab.
Periksa apakah script kamu memakai ulang koneksi. Script yang membuka koneksi TCP + TLS baru untuk setiap request sedang mengukur sesuatu yang tidak pernah dilakukan aplikasi asli. Bedanya bisa 200ms per request — cukup besar untuk membuat sistem yang sehat terlihat gagal, atau membebani load generator sampai dia sendiri yang jadi bottleneck.
Pisahkan angka klien dari angka server. Response time yang dicatat load generator mencakup jaringan. Metrik di sisi server tidak. Ketika keduanya berbeda jauh, selisih itu bukan gangguan — selisih itu justru datanya. Dia memberi tahu kamu bahwa masalahnya ada di jaringan atau di antrean sebelum aplikasi, bukan di dalam aplikasi.
Latihan minggu ini: Ambil satu transaksi yang paling sering diuji tim. Gambar sepuluh tahapnya di selembar kertas dan isi perkiraan waktu tiap tahap. Tandai tahap mana yang bisa diubah oleh tim pengembang, dan mana yang tidak. Bawa gambar itu ke diskusi mingguan — hampir pasti akan ada dua orang yang menggambarnya berbeda, dan perbedaan itu adalah percakapan yang paling berharga di sesi ini.
7. Pertanyaan untuk ronde berikutnya
Pak Andi mendapat jawabannya dalam 1,4 detik, dan kita sudah tahu ke mana setiap milidetik pergi.
Sekarang bayangkan jika bukan hanya Pak Andi.
Bayangkan jika 500 orang menekan tombol yang sama dalam detik yang sama. Jaringannya sama cepatnya. Server-nya belum penuh. Query-nya masih query yang sama, masih 15ms, masih memakai index yang sama.
Tapi Pak Andi sekarang menunggu 4 detik.
Tidak ada satu pun dari sepuluh tahap tadi yang menjadi lebih lambat. Semuanya masih bekerja persis seperti sebelumnya.
Jadi dari mana datangnya 2,6 detik tambahan itu?
Jawabannya bukan di daftar sepuluh tahap itu. Jawabannya ada di ruang di antara tahap-tahap tersebut — tempat yang belum kita lihat sama sekali.
Itu isi Sesi 2: Ke Mana Waktu Pergi.
SYSTEM UNDER PRESSURE · Sesi 1 dari 34