CAP Theorem Trade-offs — System Design

CAP Theorem menyatakan bahwa dalam sistem terdistribusi, kamu hanya bisa menjamin 2 dari 3 properti secara bersamaan: Consistency (C) /\ / \ / \ / ?? \ / \ /…

CAP theorem bilang: kalau jaringan antara dua node database terputus, sistem harus memilih antara konsistensi (semua yang membaca melihat data terbaru, atau dapat error) dan availability (semua request tetap dijawab, walau datanya mungkin lama). Bayangin TiketNada punya dua loket di stadion. Keduanya memegang daftar kursi yang sama dan saling mengabari lewat walkie-talkie. Suatu saat walkie-talkie mati. Pilihan pertama: kedua loket berhenti menjual sampai walkie-talkie nyala lagi. Gak ada kursi terjual dua kali, tapi pembeli menunggu. Pilihan kedua: tiap loket tetap menjual dari daftarnya sendiri. Gak ada yang menunggu, tapi kursi yang sama bisa terjual dua kali, dan harus dibereskan nanti. Gak ada pilihan ketiga.

🧪 Tebakan cepat: database kursi TiketNada punya replika di Jakarta dan Singapura, dan jaringan di antara keduanya putus. Sistemnya memilih tetap menerima tulisan. Budi membeli kursi A12 lewat Jakarta, lalu Siti membeli A12 lewat Singapura. Setelah jaringan pulih, A12 milik siapa? Tebak dulu, jawabannya ada di bagian "Setelah jaringan pulih".

C, A, dan P

Rumusan populernya: sistem terdistribusi hanya bisa menjamin 2 dari 3 properti itu secara bersamaan. Arti tepatnya lebih sempit: P bukan pilihan, karena jaringan pasti pernah putus (lesson Kenapa Distributed System Sulit). Jadi pertanyaannya cuma satu: saat partisi terjadi, kamu korbankan C atau A? Saat jaringan sehat, sistem bisa punya keduanya.

Saat jaringan putus: CP atau AP

Replika jkt (Jakarta) dan sgp (Singapura) menyimpan kursi A12 Konser Nusantara dan jumlah "suka" konser itu: 1.200. Angka model contoh: tulisan lokal 2 ms, bolak-balik Jakarta ke Singapura (RTT) 20 ms. Jaringan di antara keduanya putus, lalu Budi membeli A12 lewat Jakarta dan Siti lewat Singapura:

CP: tolak tulisan AP: terima, berbeda dulu
Budi beli A12 di jkt 503 setelah 500 ms: sgp gak terjangkau OK dalam 2 ms, sgp belum tahu
Siti beli A12 di sgp 503 setelah 500 ms OK dalam 2 ms, jkt belum tahu
Rina membaca A12 di sgp kosong (benar: belum ada yang membeli) Siti (Jakarta bilang Budi)
Selama putus aman, tapi gak bisa jual tetap jual, data dua replika berbeda

CP (consistency + partition tolerance) memilih menolak: tulisan baru dianggap berhasil hanya kalau kedua replika menyimpannya, dan saat salah satu gak terjangkau, tulisan ditolak setelah timeout. AP (availability + partition tolerance) memilih menjawab: tiap replika menerima tulisan sendiri, dan replikasinya menyusul. Data yang berbeda lalu disamakan belakangan: eventual consistency.

Setelah jaringan pulih

Di AP, kedua replika saling mengirim tulisan yang tertunda, lalu harus rekonsiliasi: menyamakan data yang berbeda.

Jawaban tebakannya: A12 jatuh ke Siti, dan Budi harus di-refund. Itu sebabnya penjualan kursi dan pembayaran memilih CP: lebih baik menolak sebentar daripada menjual satu kursi ke dua orang. Jumlah suka, feed, dan keranjang belanja boleh AP.

// TiketNada: kursi A12 dan jumlah suka di dua replika (model contoh)
const MODE = "AP"; // CP: tolak tulisan saat putus. AP: terima, rekonsiliasi nanti
const RTT = 20; // ms bolak-balik Jakarta ke Singapura
let putus = false;
let jam = 0; // waktu virtual, ms
const replika = {
  jkt: { kursiA12: null, suka: { jkt: 700, sgp: 500 } },
  sgp: { kursiA12: null, suka: { jkt: 700, sgp: 500 } },
};
const tertunda = []; // tulisan AP yang belum sampai ke sisi lain
const lain = (sisi) => (sisi === "jkt" ? "sgp" : "jkt");
const totalSuka = (r) => r.suka.jkt + r.suka.sgp;

function terapkan(r, w) {
  if (w.jenis === "suka") r.suka[w.asal] = Math.max(r.suka[w.asal], w.nilai);
  else if (!r.kursiA12 || r.kursiA12.t < w.t) r.kursiA12 = { nama: w.nama, t: w.t }; // last-writer-wins
}

function tulis(sisi, w, label) {
  w.t = jam;
  if (MODE === "CP") {
    if (putus) return console.log(`${label} di ${sisi}: 503 setelah 500 ms, ${lain(sisi)} gak terjangkau`);
    terapkan(replika[sisi], w);
    terapkan(replika[lain(sisi)], w); // tunggu replika lain: sinkron
    return console.log(`${label} di ${sisi}: OK dalam ${2 + RTT} ms`);
  }
  terapkan(replika[sisi], w); // AP: tulis lokal, langsung jawab
  if (putus) tertunda.push({ ke: lain(sisi), w });
  else terapkan(replika[lain(sisi)], w); // replikasi async menyusul
  console.log(`${label} di ${sisi}: OK dalam 2 ms${putus ? `, ${lain(sisi)} belum tahu` : ""}`);
}

function beli(sisi, nama) {
  jam += 1000;
  const pemilik = replika[sisi].kursiA12;
  if (pemilik) return console.log(`${nama} beli A12 di ${sisi}: 409, sudah milik ${pemilik.nama}`);
  tulis(sisi, { jenis: "kursi", nama }, `${nama} beli A12`);
}

function suka(sisi) {
  jam += 1000;
  tulis(sisi, { jenis: "suka", asal: sisi, nilai: replika[sisi].suka[sisi] + 1 }, "suka +1");
}

function jaringan(nyala) {
  jam += 1000;
  putus = !nyala;
  if (putus) return console.log("jaringan putus");
  console.log(`jaringan pulih: kirim ${tertunda.length} tulisan tertunda`);
  const a = replika.jkt.kursiA12;
  const b = replika.sgp.kursiA12;
  if (a && b && a.nama !== b.nama) console.log(`konflik A12: ${a.nama} vs ${b.nama}, menang ${a.t > b.t ? a.nama : b.nama}`);
  for (const { ke, w } of tertunda.splice(0)) terapkan(replika[ke], w);
  console.log(`jkt dan sgp sama lagi: A12 ${replika.jkt.kursiA12?.nama ?? "kosong"}, suka ${totalSuka(replika.jkt)}`);
}

jaringan(false);
beli("jkt", "Budi");
beli("sgp", "Siti");
suka("jkt");
suka("sgp");
jaringan(true);

Output-nya:

jaringan putus
Budi beli A12 di jkt: OK dalam 2 ms, sgp belum tahu
Siti beli A12 di sgp: OK dalam 2 ms, jkt belum tahu
suka +1 di jkt: OK dalam 2 ms, sgp belum tahu
suka +1 di sgp: OK dalam 2 ms, jkt belum tahu
jaringan pulih: kirim 4 tulisan tertunda
konflik A12: Budi vs Siti, menang Siti
jkt dan sgp sama lagi: A12 Siti, suka 1202

Mitos "pilih 2 dari 3"

Gambar segitiga CAP sering membuat orang mengira ada tiga paket: CP, AP, atau CA. CA artinya konsisten dan selalu menjawab, tapi gak tahan partisi. Itu cuma mungkin kalau gak ada jaringan yang bisa putus, yaitu satu server saja, dan itu bukan sistem terdistribusi. "Di satu region cloud partisi gak terjadi" juga keliru: switch, kabel, dan konfigurasi tetap bisa gagal. Pilihan nyatanya hanya CP atau AP, dan pilihan itu baru berlaku saat partisi.

PACELC: saat jaringan sehat pun ada harga

PACELC melengkapi CAP: kalau ada Partisi, pilih A atau C; Else (jaringan sehat), pilih Latency atau Consistency. Di model TiketNada: CP menunggu replika lain di setiap tulisan, jadi tulisan butuh 2 + 20 = 22 ms. AP menjawab dalam 2 ms, tapi replika Singapura baru menerima tulisan itu sekitar 10 ms kemudian, dan pembacaan di sana sebelum itu mendapat data lama. Jadi biaya konsistensi dibayar setiap hari, bukan cuma saat jaringan putus. Bandingkan dengan sync dan async di lesson Replication & HA (track Database).

Contoh sistem

🤔 Di antara kuat dan eventual

Ada level konsistensi di tengah: read-your-writes (kamu selalu melihat tulisanmu sendiri), monotonic reads (gak pernah melihat data yang lebih lama dari yang sudah kamu lihat), dan causal consistency (balasan gak pernah muncul sebelum pesan yang dibalas). Contoh: setelah Budi menekan "suka", dia sendiri harus langsung melihat 1.201, walau orang lain boleh melihat 1.200 beberapa milidetik lagi.

⚠️ Jebakan yang sering ketemu

TL;DR: CAP: saat network partition, sistem terdistribusi harus memilih antara consistency (setiap pembacaan melihat tulisan terbaru, atau error) dan availability (setiap request dijawab, walau datanya lama). Rumusan populernya "hanya bisa menjamin 2 dari 3 (C, A, P) sekaligus", tapi P bukan pilihan, jadi CA bukan opsi nyata: pilihannya CP atau AP. CP menolak tulisan saat replika lain gak terjangkau (TiketNada: 503 setelah 500 ms), cocok untuk kursi, pembayaran, saldo, lock (etcd, ZooKeeper, HBase). AP tetap menerima, data berbeda dulu lalu direkonsiliasi (eventual consistency), cocok untuk suka, feed, keranjang (Cassandra, DynamoDB): counter bisa digabung (G-Counter: 1.202), kursi yang terjual dua kali butuh refund. PACELC: saat jaringan sehat pun ada pilihan latency atau consistency (CP 22 ms, AP 2 ms dengan replika yang telat).

Yang akan kamu pelajari