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
- Consistency (C): setiap pembacaan mendapat tulisan terbaru, atau error. Semua node terlihat seperti satu salinan data.
- Availability (A): setiap request ke node yang hidup mendapat jawaban yang bukan error, walau isinya mungkin belum terbaru.
- Partition tolerance (P): sistem tetap berjalan walau pesan antarnode hilang karena jaringan terbelah (network partition).
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.
- Jumlah suka gampang. Tiap replika mencatat tambahan dari sisinya sendiri (Jakarta +1, Singapura +1), dan saat digabung, keduanya dijumlah: 1.202. Struktur ini disebut G-Counter, salah satu CRDT (struktur data yang selalu bisa digabung tanpa konflik). Kalau yang disimpan cuma satu angka, "yang terakhir menang" akan menghasilkan 1.201: satu suka hilang.
- Kursi A12 gak bisa digabung: satu kursi, dua pemilik. Aturan last-writer-wins memberi A12 ke Siti karena tulisannya lebih belakangan, dan pembelian Budi hilang diam-diam padahal dia sudah membayar.
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
MODEmenentukan pilihannya. Ganti jadi"CP", dan keempat tulisan selama putus dijawab503.tertundaadalah tulisan AP yang belum sampai ke replika lain. Semuanya dikirim dijaringan(true).sukamenyimpan hitungan per asal (jkt,sgp), danterapkanmengambil yang terbesar per asal. Itu G-Counter: tambahan dari dua sisi gak saling menimpa.kursiA12memakai last-writer-wins: tulisan dengantlebih besar menang, dan yang kalah hilang tanpa error.
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
- CP: etcd, ZooKeeper, HBase. Dipakai untuk konfigurasi, lock, dan pemilihan leader, di mana jawaban yang salah lebih berbahaya daripada gak menjawab (lesson Consensus Algorithms).
- AP: Cassandra, DynamoDB (dengan pembacaan eventually consistent, default-nya), CouchDB. Tetap menjawab saat partisi, datanya menyusul.
- Banyak database bisa diatur. Cassandra dengan
QUORUMdan MongoDB denganmajoritybergeser ke arah CP. Jadi yang dipilih sebenarnya perilaku per operasi, bukan merek database.
🤔 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
- Menganggap CA itu pilihan. Di sistem terdistribusi, partisi pasti terjadi. Pilihannya CP atau AP.
- Satu pilihan untuk seluruh aplikasi. Kursi dan pembayaran CP, jumlah suka dan feed AP, di aplikasi yang sama.
- AP tanpa rencana rekonsiliasi. Last-writer-wins diam-diam membuang tulisan. Pikirkan dulu data mana yang bisa digabung (counter) dan mana yang butuh aturan bisnis (refund).
- Lupa sisi latency. CP membayar RTT antarreplika di setiap tulisan, bahkan saat jaringan sehat.
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).