Distributed system adalah sistem yang bagian-bagiannya jalan di beberapa komputer dan saling bicara lewat jaringan. Bayangin kamu pesan makanan ke warung lewat chat: "2 nasi goreng ya". Lima menit gak ada balasan. Pesannya gak sampai? Sudah sampai, lagi dimasak, tapi balasannya gak terkirim? Atau warungnya cuma lagi rame? Dari HP-mu, ketiganya terlihat sama: sepi. Kalau kamu kirim ulang, bisa-bisa datang 4 nasi goreng. Itu masalah inti distributed system.
🧪 Tebakan cepat: service
orderminta servicebayarmemotong kartu Budi Rp1.500.000, lalu menunggu 2 detik tanpa jawaban. Kartu Budi sudah terpotong atau belum? Tebak dulu, jawabannya ada di bagian "Timeout: tiga kejadian, satu gejala".
TiketNada: dari satu server jadi banyak
Sepanjang track ini kita merancang TiketNada, aplikasi jual tiket konser. Konser yang dijual: Konser Nusantara, 10.000 tiket, Rp750.000 per tiket, penjualan dibuka jam 10.00 WIB. Jam segitu ratusan ribu orang buka aplikasi bersamaan: "war tiket".
Awalnya TiketNada cukup satu server: kode aplikasi dan database di satu mesin. Kalau mati, ya mati semua; kalau hidup, semuanya konsisten. Debug cukup baca satu log. Masalahnya, satu mesin punya batas. Jadi TiketNada dipecah: beberapa server aplikasi, database sendiri, dan pembayaran jadi service sendiri bernama bayar. Begitu ada dua komputer yang harus bekerja sama lewat jaringan, kamu sudah punya distributed system.
Di lesson ini: Budi memesan 2 tiket, pesanan #1001, total Rp1.500.000. Service order mengirim POST /charges ke service bayar. Angka waktunya model contoh: jaringan 10 ms sekali jalan, bayar butuh 120 ms untuk memotong kartu, dan order menyerah (timeout) setelah 2.000 ms.
Timeout: tiga kejadian, satu gejala
Timeout artinya batas waktu menunggu jawaban. Lewat dari itu, pemanggil berhenti menunggu dan menganggap gagal. Ini tiga kejadian berbeda di jaringan:
| Kejadian | Yang sebenarnya terjadi di bayar |
Yang dilihat order di ms 2.000 |
|---|---|---|
| Request hilang | gak pernah terima apa-apa, kartu utuh | timeout |
| Response hilang | kartu terpotong di ms 130, jawaban 201 hilang di jalan |
timeout |
bayar lambat |
sibuk, kartu baru terpotong di ms 2.610 | timeout |
Kolom terakhir sama persis. Jadi jawaban tebakannya: gak bisa tahu. Dari sisi order, "request gak sampai", "sudah diproses tapi jawabannya hilang", dan "masih diproses" gak bisa dibedakan. Ini aturan pahit distributed system: jaringan bisa gagal kapan saja, dan pemanggil sering gak tahu bagian mana yang gagal.
Retry bisa memotong kartu dua kali
Reaksi wajar setelah timeout adalah retry: kirim ulang. Ini timeline "response hilang" dengan retry setelah jeda 100 ms:
| ms | order |
bayar |
|---|---|---|
| 0 | kirim POST /charges Rp1.500.000 |
|
| 10 | terima, mulai memotong kartu | |
| 130 | kartu terpotong, kirim 201 (hilang di jalan) |
|
| 2.000 | timeout | |
| 2.100 | retry: kirim lagi | |
| 2.230 | request "baru", kartu terpotong lagi | |
| 2.240 | terima 201, tampilkan "sukses" |
Budi kehilangan Rp3.000.000, padahal order merasa semuanya lancar. Obatnya: setiap aksi diberi nomor unik, Idempotency-Key, yang dipakai lagi saat retry. bayar menyimpan hasil per key. Kalau key yang sama datang lagi, bayar mengirim ulang hasil yang tersimpan tanpa memotong kartu.
// order: bayar pesanan #1001, timeout 2 detik, retry sekali
async function bayarPesanan() {
const key = "ord-1001"; // satu key untuk satu aksi, dipakai lagi saat retry
for (let percobaan = 1; percobaan <= 2; percobaan++) {
try {
const res = await fetch("https://bayar.tiketnada.test/charges", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ pesanan: 1001, jumlah: 1500000 }),
signal: AbortSignal.timeout(2000), // menyerah setelah 2.000 ms
});
return res.status; // 201, atau 409 kalau yang pertama masih diproses
} catch (err) {
if (percobaan === 2) throw err; // dua kali gagal: lapor gagal
await new Promise((r) => setTimeout(r, 100)); // jeda sebelum retry
}
}
}
AbortSignal.timeout(2000)membatalkanfetchsetelah 2.000 ms. Itu timeout-nya.- Loop
fordengan dua percobaan: kirim, dan kalau gagal, kirim sekali lagi setelah jeda 100 ms. "Idempotency-Key": keyadalah key yang sama di kedua percobaan. Dengan key ini, retry di kasus "response hilang" mendapat201yang tersimpan, kartu cuma terpotong sekali.- Di kasus "
bayarlambat", retry datang saat yang pertama masih diproses, jadibayarmenjawab409 Conflict: "key ini masih diproses". Kartu tetap terpotong sekali.
Cara kerja key-nya dibahas tuntas di lesson Idempotency (track API).
Partial failure: sebagian mati, sebagian hidup
Di satu server, sistemmu hidup atau mati, gak ada tengahnya. Di banyak server ada keadaan ketiga: partial failure, sebagian komponen rusak sementara yang lain jalan. Misalnya bayar punya dua salinan, bayar-1 dan bayar-2, dan proses di bayar-2 crash. Request yang kebetulan ke bayar-2 gagal, yang ke bayar-1 sukses. Pengguna melihatnya sebagai "kadang bisa, kadang error".
Kabar baiknya, crash yang bersih itu kasus mudah: mesinnya menolak koneksi dalam 20 ms (ECONNREFUSED), jadi order tahu request-nya belum sampai dan aman dikirim ke bayar-1. Yang sulit justru server yang hidup tapi lambat atau macet: dia kelihatan seperti timeout lagi. Mendeteksi server bermasalah dan menjauhkan traffic darinya adalah tugas load balancer dengan health check (lesson Load Balancing).
📌 Delapan anggapan yang keliru
Tahun 1990-an, engineer di Sun Microsystems mencatat anggapan yang bikin program terdistribusi rusak. Dikenal sebagai fallacies of distributed computing:
- Jaringan selalu bisa diandalkan.
- Latency nol.
- Bandwidth tak terbatas.
- Jaringan aman.
- Susunan jaringan gak berubah.
- Cuma ada satu admin.
- Mengirim data itu gratis.
- Semua bagian jaringan seragam.
Timeline tadi sudah membantah nomor 1 dan 2. Tiap pattern di track ini adalah cara hidup tanpa anggapan itu.
Jam yang gak bisa dipercaya
Tiap server punya jam sendiri, dan jam-jam itu gak pernah sama persis (clock skew). NTP menyamakannya sampai selisih beberapa milidetik, tapi jam tetap bisa melenceng. Kalau server A mencatat pesanan jam 10:00:00,123 dan server B jam 10:00:00,089, kamu gak bisa yakin B duluan: bisa saja jam B tertinggal 50 ms.
Karena itu urutan kejadian antarserver memakai logical clock, penghitung yang naik tiap ada kejadian:
- Lamport timestamp: tiap server punya satu angka. Naik 1 tiap kejadian. Saat menerima pesan, angkanya jadi
max(punyaku, punya pengirim) + 1. Kalau X menyebabkan Y, angka X pasti lebih kecil. Tapi angka lebih kecil belum tentu berarti duluan: dua kejadian yang gak saling tahu bisa dapat angka mirip. - Vector clock: tiap server menyimpan satu angka per server, misalnya
[A:1, B:0]dan[A:0, B:1]. Dari situ kelihatan bahwa dua kejadian ini concurrent: gak ada yang duluan, karena gak ada yang lebih besar di semua posisi. Itu yang gak bisa dibedakan Lamport. Harganya, ukurannya tumbuh sesuai jumlah server.
Split brain: dua bos sekaligus
Ada keputusan yang harus diambil satu pihak, misalnya "kursi A-12 jatuh ke siapa". Biasanya satu server jadi leader. Kalau jaringan terputus (network partition) dan cluster terbelah dua, tiap sisi bisa mengira sisi lain mati lalu mengangkat leader sendiri. Itu split brain: dua leader membuat keputusan yang bertentangan, misalnya kursi yang sama dijual ke dua orang, dan susah didamaikan saat jaringan pulih. Algoritma konsensus seperti Raft mencegahnya dengan quorum: keputusan sah hanya kalau disetujui mayoritas, dan cuma satu sisi yang bisa punya mayoritas.
🎯 Kompas: masalah dan pattern-nya
| Masalah | Pattern yang menjawab | Dibahas di |
|---|---|---|
| Partial failure | load balancer + health check | Load Balancing |
| Timeout dan retry | idempotency key, retry dengan jeda | Idempotency Design |
| Data beda antarserver | replikasi, model consistency | Database Scaling, CAP Theorem |
| Siapa yang memutuskan | leader election, quorum | Consensus Algorithms |
| Jam berbeda | logical clock | lesson ini |
| Bug lewat banyak service | distributed tracing | Monitoring dan Logging |
⚠️ Jebakan yang sering ketemu
- Menganggap timeout berarti gagal. Timeout cuma berarti "gak tahu". Operasinya bisa sudah sukses di seberang.
- Retry tanpa idempotency key. Untuk aksi yang mengubah data (bayar, pesan), retry tanpa key bisa menggandakan aksinya.
- Mengurutkan kejadian antarserver pakai timestamp jam dinding. Jam tiap server berbeda. Pakai logical clock atau satu sumber urutan.
- Mengira "tambah server = pasti lebih cepat". Tiap server tambahan berarti lebih banyak panggilan jaringan, koordinasi, dan cara untuk gagal.
TL;DR: Distributed system adalah komputer-komputer yang bekerja sama lewat jaringan, seperti order dan bayar di TiketNada. Jaringan bisa kehilangan request, kehilangan response, atau lambat, dan ketiganya terlihat sama bagi pemanggil: timeout. Jadi timeout artinya "gak tahu", bukan "gagal". Retry tanpa Idempotency-Key bisa memotong kartu dua kali; dengan key yang sama di tiap percobaan, aksinya terjadi sekali. Partial failure artinya sebagian komponen mati sementara yang lain hidup. Jam antarserver gak bisa dipercaya untuk urutan, jadi pakai logical clock: Lamport memberi urutan sebab-akibat, vector clock juga bisa mengenali kejadian concurrent. Split brain terjadi saat partition membuat dua leader; quorum mayoritas mencegahnya.