Kenapa Distributed System Sulit? — System Design

Sebelum kita membahas arsitektur canggih seperti load balancer, CDN, atau microservices, kita harus paham satu hal: kenapa menambah server bisa membuat sistem…

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 order minta service bayar memotong 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
    }
  }
}

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:

  1. Jaringan selalu bisa diandalkan.
  2. Latency nol.
  3. Bandwidth tak terbatas.
  4. Jaringan aman.
  5. Susunan jaringan gak berubah.
  6. Cuma ada satu admin.
  7. Mengirim data itu gratis.
  8. 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:

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

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.

Yang akan kamu pelajari