Jakarta vs Singapura: Latensi PaaS untuk Pengguna Indonesia

Foto oleh yohanes budiyanto via Wikimedia Commons (CC BY 2.0)
Hitung sendiri daripada mengutip angka. Jarak Jakarta ke Singapura sekitar 900 km, dan pada kecepatan sekitar 200.000 km per detik yang ditempuh sinyal di fibre telekomunikasi, itu memberi batas bawah round trip sekitar sembilan milidetik, batas yang tidak pernah dicapai jalur nyata karena kabel tidak lurus dan setiap handoff peering menambah delay. Ukur endpoint Anda sendiri dengan timing write-out curl dari jaringan yang benar-benar dipakai pengguna Anda.
Karena satu cold request HTTPS bukan satu round trip. DNS, TCP three-way handshake di RFC 9293, TLS 1.3 handshake di RFC 8446, lalu request HTTP-nya sendiri masing-masing memakan satu round trip, jadi selisihnya dikalikan kira-kira empat sebelum handler Anda berjalan. Kalau response-nya lebih dari sekitar 14 KB, initial window sepuluh segment yang direkomendasikan RFC 6928 menambah satu lagi.
Pakai variabel write-out curl: time_namelookup, time_connect, time_appconnect, time_starttransfer dan time_total. Semuanya kumulatif dari awal transfer, jadi setiap leg adalah hasil pengurangan — biaya TLS adalah time_appconnect dikurangi time_connect, bukan time_appconnect itu sendiri. Tambahkan opsi --resolve supaya resolver DNS yang lambat tidak masuk ke leg jaringan, dan laporkan median dari sekitar dua puluh sampel, bukan angka tercepat.
Untuk static asset, sebagian besar ya: script, style, font dan gambar datang dari edge yang dekat dengan pengguna dan lokasi origin berhenti berpengaruh saat cache hit. Untuk panggilan yang tidak ter-cache, tidak ada yang berubah, karena POST tidak pernah di-cache dan GET yang dipersonalisasi biasanya juga tidak, jadi request itu tetap sampai ke origin. Jalur API tanpa cache itulah satu-satunya bagian yang benar-benar digeser oleh pindah region.
Saat Anda butuh katalog managed service provider yang lengkap, versi engine database yang lebih baru, shape instance large-memory atau GPU, atau kapasitas dan batas quota yang lebih besar daripada yang ditawarkan region dalam negeri. Trafik keep-alive yang sudah warm antar service Anda sendiri juga jauh lebih tidak terasa dibanding cold page load dari mobile. Memisahkan deployment biasanya lebih baik daripada memindahkan semuanya.

Foto oleh yohanes budiyanto via Wikimedia Commons (CC BY 2.0)
Ringkasan Utama
Menaruh produk Indonesia di Singapura berarti ada sekitar 900 km fibre, sebagian besar bawah laut, antara server dan pengguna, dan satu cold request HTTPS membayar round trip itu empat kali: DNS, TCP, TLS, lalu request-nya sendiri. Ukur setiap leg dengan timing write-out curl sebelum berdebat soal region mana yang menang.
Region Singapura hampir tidak pernah benar-benar dipilih. Di situlah managed Postgres sudah berada, di situlah dropdown region berhenti secara default, dan di situlah tutorial menyuruh Anda klik. Jadi produk Indonesia berakhir dilayani dari negara lain karena kebetulan, dan penggunanya membayar kebetulan itu di setiap request, dalam milidetik yang tidak pernah masuk ticket siapa pun.
Tulisan ini adalah aritmetika di balik keputusan itu, bukan sebuah benchmark. Saya sengaja tidak mencantumkan angka round-trip time yang tidak saya ukur sendiri. Yang ada di bawah adalah batas fisik yang bisa Anda hitung dari satu konstanta bersumber, alasan jalur jaringan nyata tidak pernah mencapainya, hitungan round trip yang melipatgandakan selisih apa pun yang Anda punya, dan satu perintah curl yang menyelesaikan perdebatan dalam waktu sekitar satu menit.
Sebelum mengukur apa pun, hitung dulu apa yang tidak bisa dilewati oleh jalur itu. Sinyal di fibre telekomunikasi merambat sekitar 200.000 km per detik, kira-kira dua per tiga kecepatan cahaya di ruang hampa, karena core silica yang di-doping punya refractive index sekitar 1,4475. Konstanta itu satu-satunya angka di tulisan ini yang tidak perlu Anda ukur, dan satu-satunya yang tidak bisa diubah oleh keputusan deployment apa pun.
# The floor comes from one cited constant: a signal in telecom fibre travels
# at about 200,000 km/s -- roughly two thirds of c, because the doped-silica
# core has a refractive index near 1.4475. Nothing in your stack beats it:
# not HTTP/3, not a bigger instance, not a faster language.
# Jakarta to Singapore is about 900 km great-circle. Real fibre is longer --
# cables follow shipping lanes and landing stations, not straight lines.
# Multiply BEFORE you divide, or bc truncates the whole thing to 0.0
echo "scale=1; 2 * 900 * 1000 / 200000" | bc
# 9.0 <- ms round trip, on a perfectly straight glass path
# Sanity-check the constant against the source's own worked example:
# Sydney to New York, 16,000 km, quoted as an 80 ms one-way minimum.
echo "scale=1; 16000 * 1000 / 200000" | bc
# 80.0 <- matches, so the constant is being used as intended
# Single-digit milliseconds is therefore the FLOOR for the sea crossing.
# It is not a target, and nothing you deploy will go below it.Jarak Jakarta ke Singapura sekitar 900 km garis lurus, jadi jalur kaca yang benar-benar lurus akan memberi round trip sekitar sembilan milidetik. Jalur seperti itu tidak ada: rute kabel submarine mengikuti jalur pelayaran, landing station dan duct yang sudah ada, sehingga fibre yang benar-benar dilalui paket jauh lebih panjang daripada garis lurusnya, dan batas bawah yang nyata berada di atas sembilan milidetik. Perlakukan angka itu sebagai batas bawah yang tidak akan pernah Anda lihat, bukan sebagai target. Nilainya ada pada satu hal: angka itu memberi tahu Anda bahwa perdebatan ini soal milidetik, bukan soal ratusan milidetik, dan apa pun yang jauh lebih besar dari itu dalam hasil pengukuran Anda ditambahkan oleh sesuatu selain jarak.
Begitu dua endpoint hanya berjarak 900 km, jarak berhenti menjadi variabel yang menarik dan jalurnya yang mengambil alih. Paket yang meninggalkan ponsel di Bekasi lalu tiba di load balancer di Singapura melewati beberapa batas administratif, dan setiap batas itu adalah tempat di mana capacity planning, peering policy atau perjanjian komersial seseorang menambah delay yang tidak ada hubungannya dengan kaca.
Konsekuensi praktisnya soal dari mana Anda mengukur. Angka yang diambil dari fibre kantor Anda, atau dari CI runner yang kebetulan berada di jaringan provider yang sama dengan endpoint-nya, menggambarkan jalur yang tidak dilalui pengguna Anda. Kalau keputusannya penting, ambil pengukuran dari access network yang menurut analytics benar-benar dipakai pengguna Anda, dan ambil lebih dari sekali.
Ini bagian yang mengubah selisih kecil menjadi besar, dan bagian yang paling sering dilewati dalam perdebatan region. Satu cold request HTTPS tidak menyeberangi laut sekali saja. Hitung flight-nya untuk browser yang baru dibuka dan belum punya cache apa pun.
Lalu leg yang tidak dihitung siapa pun. RFC 6928 merekomendasikan initial congestion window sepuluh segment dengan batas 14.600 byte, jadi kira-kira 14 KB pertama dari sebuah response tiba dalam satu flight dan sisanya menunggu window tumbuh. Response JSON 60 KB bukan satu round trip; itu beberapa round trip. Jumlahkan: satu cold request membayar penyeberangan empat kali sebelum body-nya mulai jalan, dan lima kali atau lebih kalau body-nya besar. Jadi jalur yang sepuluh milidetik lebih pendek tidak bernilai sepuluh milidetik bagi pengguna Anda. Pada koneksi yang masih cold nilainya kira-kira empat kali itu, dan justru karena itulah perdebatan ini layak dilakukan.

curl sudah memecah satu request menjadi tepat leg yang dihitung di atas, dan variabel write-out-nya membuka semuanya. Satu hal yang wajib benar: timer itu kumulatif dari awal transfer, jadi setiap leg adalah hasil pengurangan, bukan angka yang langsung dibaca. Melaporkan time_appconnect sebagai biaya TLS adalah cara paling umum salah membaca output ini, karena nilai itu juga sudah mengandung DNS dan TCP handshake.
# curl's write-out timers are CUMULATIVE from the start of the transfer, so
# every leg is a subtraction, not a reading. Missing that is why people report
# "TLS takes 300 ms" when they mean "everything up to and including TLS did".
cat > /tmp/curl-format.txt <<'EOF'
dns %{time_namelookup}
tcp_done %{time_connect}
tls_done %{time_appconnect}
req_sent %{time_pretransfer}
first_byte %{time_starttransfer}
total %{time_total}
EOF
# --resolve pins the address so a slow resolver stops polluting the network
# legs. -4 stops an IPv6 path and an IPv4 path being averaged together.
curl -sS -o /dev/null -w "@/tmp/curl-format.txt" \
--resolve api.example.com:443:203.0.113.10 \
-4 https://api.example.com/health
# Read the output as differences, in this order:
# time_namelookup DNS
# time_connect - time_namelookup TCP handshake, about 1 RTT
# time_appconnect - time_connect TLS handshake, 1 RTT on TLS 1.3
# time_starttransfer - time_appconnect request out, first byte back:
# 1 RTT PLUS your handler's time
# time_total - time_starttransfer the rest of the response body# One sample is noise. Twenty samples and a median is an argument.
# Report the median, not the minimum: the minimum is the moment the path
# happened to be empty, and no real user gets that moment.
for i in $(seq 1 20); do
curl -sS -o /dev/null -4 -w "%{time_connect}\n" \
https://api.example.com/health
done | sort -n | awk '{a[NR]=$1} END {print "median tcp:", a[int(NR/2)+1]}'
# time_connect on a fresh connection is DNS plus one round trip, so this is
# the cheapest honest proxy for RTT that also proves the port is reachable.
# ping is cheaper still, and measures something your application never does.Dua flag membuat angkanya berarti. Opsi --resolve mengikat alamatnya supaya resolver yang lambat atau jauh tidak mengotori leg jaringan yang ingin Anda bandingkan, dan -4 mencegah jalur IPv4 dan IPv6 dirata-ratakan menjadi angka yang tidak menggambarkan keduanya. Setelah itu ambil sampel dengan benar: satu angka itu noise, sedangkan median dari dua puluh angka bisa Anda taruh di dokumen keputusan.
Jangan memindahkan region hanya karena hasil ping. ping mengukur satu round trip lewat kernel, sedangkan request terburuk Anda melakukan minimal empat round trip lewat TLS terminator dan aplikasi. Lebih buruk lagi, ping dari laptop Anda sendiri di fibre kantor mengukur jalur yang tidak dilalui pengguna. Kalau Anda memang siap membayar sebuah migrasi untuk ini, luangkan sepuluh menit dulu mengambil timing curl dari jaringan yang menurut analytics menjadi sumber trafik Anda.
Angka empat round trip itu adalah kasus cold, dan untuk banyak trafik kasus cold justru jarang. Koneksi yang tetap terbuka membayar DNS, TCP dan TLS sekali saja, lalu hanya butuh satu round trip per panggilan. Itulah yang dilakukan HTTP client di backend Anda sepanjang hari kalau keep-alive aktif, dan itulah sebabnya selisih region yang sama bisa jadi masalah serius untuk page load pertama tapi hampir tidak relevan untuk service yang memanggil database di region yang sama.
# The same endpoint TWICE in one curl invocation. The second transfer reuses
# the open connection, so it pays neither the TCP nor the TLS handshake --
# which is what a keep-alive HTTP client in your backend does all day long.
curl -sS -o /dev/null -o /dev/null -4 \
-w "connects=%{num_connects} tls=%{time_appconnect} ttfb=%{time_starttransfer}\n" \
https://api.example.com/health \
https://api.example.com/health
# What to look for on the SECOND line: num_connects reports 0, because no new
# connection was opened, and time_appconnect stops pulling away from
# time_connect. The crossing is now paid once per call instead of four times.
#
# So measure both cases and label them. A cold mobile page load and a warm
# server-to-server call are two different latency stories, and quoting the
# cold number for warm traffic overstates what changing region would buy.TLS sendiri punya dua tuas yang perlu diketahui. Session resumption melewati pertukaran certificate saat reconnect, dan RFC 8446 menambahkan mode 0-RTT yang menghemat satu round trip saat connection setup untuk sebagian application data — dengan harga yang nyata, karena data itu punya properti keamanan yang lebih lemah dan bisa di-replay, jadi bukan mekanisme untuk request yang memindahkan uang. HTTP/3 menyatukan setup transport dan crypto lalu melakukan multiplexing stream di satu koneksi, yang menghilangkan handshake tapi tidak menghilangkan jarak. Tidak satu pun membuat penyeberangannya lebih pendek; semuanya hanya mengurangi berapa kali Anda membayarnya.
Sebelum membandingkan region, periksa dulu apakah stack Anda sendiri memang sedang membayar harga cold. Pastikan keep-alive aktif di setiap HTTP client yang Anda punya, pastikan HTTP/2 atau HTTP/3 ternegosiasi di edge, dan pastikan response API Anda tetap di bawah sekitar 14 KB di tempat yang memungkinkan. Tiga pemeriksaan itu gratis, berlaku di region mana pun, dan sering mengembalikan lebih banyak daripada pindah region.
Latensi hanya satu baris dalam tabel yang punya beberapa baris, dan bersikap jujur soal baris lainnya justru yang membuat argumen latensi bisa dipercaya. Ini perbandingan yang akan saya sampaikan ke sebuah tim, dengan baris yang benar-benar dimenangkan hosting di Indonesia dipisahkan dari baris yang tidak.
| Apa yang sedang Anda pilih | Hosting di Indonesia | Hosting di Singapura |
|---|---|---|
| Batas bawah fibre ke pengguna Jakarta | Hanya fibre metro, tanpa penyeberangan laut yang harus dibayar | Sekitar sembilan milidetik round trip pada kondisi teoretis terbaik, lebih tinggi di kenyataan |
| Satu cold request HTTPS | Empat round trip metro | Empat penyeberangan laut, jadi selisih apa pun dikali empat |
| Panggilan API keep-alive yang sudah warm | Satu round trip metro per panggilan | Satu penyeberangan laut per panggilan, jauh lebih ringan |
| Katalog managed database | Lebih sedikit engine dan versi, jadi periksa daftarnya sebelum berkomitmen | Katalog lengkap provider, biasanya region pertama yang dapat engine baru |
| Kapasitas dan bentuk instance | Pool lebih kecil, dan shape large-memory atau GPU sering tidak tersedia | Pool dalam, lebih banyak shape, spot capacity, batas quota lebih tinggi |
| Static asset di belakang CDN | Tidak ada selisih terukur, edge yang menjawab | Tidak ada selisih terukur, edge yang menjawab |
| Panggilan API yang tidak ter-cache | Satu-satunya baris yang benar-benar digeser oleh pindah region | Membayar penyeberangan di setiap panggilan, selamanya |
Baca baris empat dan lima sebelum baris satu sampai tiga. Tim yang pindah ke dalam negeri lalu menemukan versi managed Postgres yang dibutuhkannya tidak tersedia berarti menukar satu masalah nyata dengan masalah yang lebih buruk. Pola yang biasanya bertahan di kenyataan adalah pemisahan: biarkan bagian yang butuh katalog lengkap milik provider tetap di tempat katalog itu ada, dan pindahkan jalur request yang sensitif latensi — API yang dipanggil aplikasi mobile Anda di setiap layar — sedekat mungkin ke pengguna sejauh platform mengizinkan.

CDN di depan static asset menghapus hampir semua selisih yang terlihat, dan itu hal termurah di daftar ini. Script, style, font dan gambar datang dari edge yang dekat dengan pengguna, lokasi origin berhenti berpengaruh saat cache hit, dan halaman mulai tergambar sebelum server Anda terlibat sama sekali. Kalau produk Anda sebagian besar berisi konten, berhenti membaca di sini dan pergilah mengatur caching dengan benar.
Untuk panggilan yang tidak ter-cache, CDN tidak melakukan apa-apa. POST tidak pernah di-cache dan GET yang dipersonalisasi biasanya juga tidak, jadi request itu tetap berjalan ke origin — dan kalau Anda mem-proxy-nya lewat edge, Anda justru menambah satu hop, karena leg pengguna ke edge sekarang berada di depan leg edge ke origin yang tetap menyeberangi laut. Menguntungkan atau tidak sepenuhnya bergantung pada apakah edge menyimpan koneksi pool yang warm ke origin Anda, yang artinya kembali lagi ke argumen reuse. Pembagian yang jujur sederhana: trafik yang bisa di-cache adalah masalah CDN, dan trafik API yang tidak ter-cache adalah satu-satunya hal yang bisa benar-benar digeser oleh pilihan region.
Aturan yang akan saya berikan ke sebuah tim itu pendek. Hitung berapa round trip yang dilakukan request terburuk Anda, ukur setiap leg-nya dengan write-out curl dari jaringan yang dipakai pengguna Anda, dan baru setelah itu bandingkan region, karena fisika menetapkan batas bawah beberapa milidetik, routing yang menentukan seberapa jauh di atas batas itu Anda mendarat, dan hitungan round trip yang menentukan seberapa besar semua itu berpengaruh. CDN memberi Anda separuh static tanpa biaya. Jalur API yang tidak ter-cache adalah satu-satunya tempat di mana pindah region benar-benar sepadan dengan migrasinya.
Sumber dan bacaan lanjutan