UNDER PRESSURE

Level 0 · Membaca Tekanan

Sesi 2: Ke Mana Waktu Pergi

Sesi 2 / 346 menit baca

1. Bayangkan jika

Bayangkan kamu datang ke sebuah kedai kopi populer pada jam delapan pagi.

Di belakang meja kasir, ada seorang barista bernama Budi. Budi sangat terampil. Dari saat kamu menyampaikan pesanan sampai struk dan kopi tercetak, Budi hanya butuh waktu 50 detik untuk melayani satu orang.

Tetapi saat kamu melangkah masuk ke pintu kedai, ada 25 orang yang berdiri berderet di depan kasir. Kamu harus berdiri di belakang mereka, bergeser pelan-pelan selangkah demi selangkah, selama 5 menit (300 detik) sebelum akhirnya giliranmu tiba di depan Budi.

Total waktu dari saat kamu masuk pintu sampai memegang cangkir kopi: 350 detik (hampir 6 menit).

Sekarang bayangkan jika pemilik kedai kopi melihat antrean mengular itu lalu mengirim Budi ke kursus barista kilat. Setelah kursus, Budi menjadi dua kali lebih cepat — dia bisa memproses pesanan dalam 25 detik, bukan lagi 50 detik.

Hasilnya? Kamu masuk kedai, antre 25 orang selama 300 detik, lalu dilayani Budi dalam 25 detik. Total waktu tunggumu sekarang: 325 detik.

Dari 350 detik menjadi 325 detik. Antrean di pintu masih mengular. Pelanggan tetap marah karena terlambat masuk kantor.

Di dunia pengembangan perangkat lunak, cerita ini terjadi setiap hari. Query database tercatat 20ms di log. Aplikasi memproses logika bisnis dalam 30ms. Tetapi pengguna di ponselnya menunggu 3 detik (3.000ms).

Ke mana 2.950 milidetik sisanya pergi?

Sesi ini membongkar mitos terbesar dalam pengukuran performa: menganggap waktu proses aplikasi adalah sama dengan waktu tunggu pengguna.


2. Apa yang sebenarnya terjadi

Ketika sebuah request dikirim oleh pengguna, total waktu yang dia rasakan — Response Time — bukanlah angka tunggal. Angka itu adalah penjumlahan dari tiga komponen yang bekerja di tempat yang berbeda:

\text{Response Time} = \text{Network Time} + \text{Queue Time} + \text{Service Time}

Mari kita bedah ketiganya satu per satu.

Komponen 1 — Network Time (Waktu Perjalanan)

Ini adalah waktu yang dihabiskan data untuk melintasi kabel, gelombang radio, router, dan TLS handshake seperti yang kita bedah di Sesi 1. Pada kondisi normal, angka ini relatif stabil selama kondisi sinyal dan lokasi pengguna tidak berubah.

Komponen 2 — Service Time (Waktu Kerja Murni)

Ini adalah waktu ketika CPU server benar-benar sedang mengeksekusi instruksi kodenya, atau saat disk/memori database sedang membaca baris data milikmu. Ini adalah "Budi sedang melayani pesananmu".

Di sebagian besar aplikasi web modern, service time untuk satu request sederhana hanya berkisar antara 5 sampai 50 milidetik.

Komponen 3 — Queue Time (Waktu Antre)

Ini adalah waktu ketika request milikmu sudah tiba di server, tetapi tidak ada CPU thread atau koneksi database yang menganggur untuk mengerjakannya. Request milikmu ditaruh di dalam memori antrean (queue buffer) dan dipaksa menunggu.

Ini adalah "25 orang yang berdiri di depan kasir".

[ Request Tiba ] ────► ░░░ ANTREAN (Queue Time) ░░░ ────► [ SERVER KERJA (Service Time) ] ────► [ Respons Keluar ]
                            2.920 ms                                    30 ms

Mengapa Queue Time Menyembunyikan Diri?

Alasan utama kenapa queue time sering tidak terdeteksi adalah tempat pencatatannya.

Banyak tim mengukur performa aplikasi dengan menaruh kode pencatat waktu (execution timer) di dalam fungsi aplikasi:

start_time = now()
result = process_order(request) # <--- Service time saja
log_duration(now() - start_time)

Timer di atas hanya mencatat service time (30ms). Timer itu tidak pernah tahu bahwa request tersebut sudah mendekam di antrean web server (misalnya NGINX atau Tomcat thread pool) selama 2.920ms sebelum fungsi process_order() dipanggil.

Di dashboard APM internal, grafik menunjukkan "rata-rata response time 30ms". Di saat yang sama, pengguna berteriak karena aplikasinya macet 3 detik. Kedua angka itu benar — mereka hanya mengukur hal yang berbeda.


3. Bagaimana jika kita coba...

Bagaimana jika kita optimasi kode aplikasinya dari 30ms menjadi 10ms?

Mari kita hitung dampaknya jika antrean sedang menumpuk 2.920ms:

  • Sebelum optimasi: 50\text{ms (network)} + 2.920\text{ms (queue)} + 30\text{ms (service)} = 3.000\text{ms}
  • Sesudah optimasi: 50\text{ms (network)} + 2.920\text{ms (queue)} + 10\text{ms (service)} = 2.980\text{ms}

Penghematan 20ms dari 3.000ms adalah perbaikan sebesar 0,66%. Tidak ada satu pun pengguna yang akan menyadarinya.

Bagaimana jika kita menaikkan spesifikasi CPU server (Vertical Scaling)?

Menaikkan CPU membuat service time lebih cepat. Namun, jika batas bottleneck berada pada jumlah thread pool atau jumlah koneksi database yang terkunci (contention), menaikkan CPU tidak mengurangi waktu antre di depan pintu. Antrean tetap menumpuk karena pintu masuknya terbatas.


4. Nama resminya

Di dokumen desain arsitektur dan laporan APM, istilah-istilah ini muncul dengan nama baku:

  • Service Time (T_s) — Waktu murni yang dibutuhkan suatu komponen untuk menyelesaikan satu unit kerja tanpa ada hambatan antrean.
  • Queue Time / Wait Time (T_q) — Durasi yang dihabiskan request berada di dalam buffer antrean sebelum mulai diproses oleh worker thread.
  • Response Time / Latency (T_r) — Total durasi dari sudut pandang pemanggil (T_r = T_s + T_q + \text{network}).
  • Tail Latency (p95 / p99) — Ukuran waktu respon pada persentil ke-95 atau ke-99. Jika p99 adalah 3 detik, artinya 1% dari total request (misal 10.000 dari 1 juta pengguna) mengalami waktu tunggu 3 detik atau lebih.
  • Queue Depth / Backlog — Jumlah request yang saat ini sedang mengantre menunggu jatah pemrosesan.

5. Di dunia kita

Bagaimana fenomena queue time ini mewujud di sistem produksi sungguhan?

  1. Tomcat / NGINX Thread Exhaustion:
    Ketika seluruh worker thread (misalnya 200 thread) sedang sibuk memproses request yang lambat, request ke-201 akan ditahan di OS socket backlog buffer. Aplikasi Java tidak mencatat latensi ini karena request ke-201 belum sampai ke servlet.

  2. Database Connection Pool Bottleneck:
    Aplikasi punya 100 worker thread, tetapi connection pool ke PostgreSQL hanya dibuka 10 koneksi. Saat 10 koneksi dipakai, 90 thread sisanya berhenti dan antre menunggu koneksi DB lepas. Di sini, queue time terjadi di dalam memori aplikasi sebelum query dikirim ke database.

  3. Jam Sibuk (Flash Sale / Gajian):
    Pada lalu lintas normal, queue time bernilai mendekati 0ms. Namun pada jam sibuk, begitu kapasitas pemrosesan terlampaui sedikit saja, antrean meledak secara eksponensial dalam hitungan detik.


6. Lensa performance tester

Sebagai penguji kinerja (performance tester), Sesi 02 mengubah cara kita membaca grafik uji beban:

  1. Jangan Percaya Rata-Rata (Average Is a Lie):
    Rata-rata menyembunyikan tail latency. Rata-rata 200ms bisa berarti 90% pengguna menunggu 50ms dan 10% pengguna menunggu 5.000ms di antrean. Selalu minta dan pantau p95 dan p99.

  2. Ukur Latensi dari Sisi Klien (Load Generator):
    Metrik kunci untuk menguji daya tahan antrean adalah Response Time yang dicatat oleh alat penguji (JMeter/K6/Gatling), bukan metrik dari APM server saja. Selisih antara angka JMeter dan angka APM adalah murni Queue Time + Network Time.

  3. Pantau Metrik Saturation (Queue Depth):
    Saat melakukan load test, jangan hanya memantau CPU & Memory. Pantau metrik antrean internal: http_req_queue_depth, db_pool_waiting_threads, dan tcp_listen_overflows.


7. Pertanyaan untuk ronde berikutnya

Kita sudah melihat bahwa ketika sistem penuh, waktu tunggu pengguna meledak dari 30ms menjadi 3 detik karena antrean (queue time).

Namun ada satu hal yang ganjil:

Mengapa saat pemakaian CPU server baru 70%, antrean sudah mulai mengular dan respon time mulai membengkak? Bukankah masih ada sisa 30% CPU yang bebas?

Mengapa kurva antrean tidak naik secara garis lurus, tetapi tiba-tiba meledak melengkung ke atas seperti dinding tegak lurus?

Jawabannya ada di matematika antrean dan Little's Law.

Itu isi Sesi 3: Antrean Tidak Pernah Bohong.


SYSTEM UNDER PRESSURE · Sesi 2 dari 34