π Topik β Consistency Models: Strong, Eventual, Read-Your-Writes Kategori: Distributed Systems Β· Level: advanced Β· Sesi 05:00 Deep Learning
π― Tujuan Belajar
Setelah sesi ini, keputusan berikut jadi lebih baik:
- Bisa memilih consistency level per-operasi, bukan per-database. Satu sistem biasanya butuh model berbeda di jalur berbeda (saldo vs feed komentar).
- Bisa membaca dokumentasi database dan tahu apa yang sebenarnya dijanjikan β "strong consistency" di marketing sheet hampir selalu lebih lemah dari yang dibayangkan.
- Bisa mendiagnosis bug anomali yang muncul sekali sebulan di production ("kok komentar saya hilang lalu muncul lagi?") sebagai masalah konsistensi, bukan "bug random".
- Bisa menolak permintaan "pokoknya harus konsisten" dengan biaya nyata: latency, availability, throughput.
π§ Penjelasan Inti
Masalah yang diselesaikan
Begitu data ada di lebih dari satu node, muncul pertanyaan: kalau saya baca dari node A sekarang, apakah saya melihat tulisan yang barusan masuk ke node B?
Tanpa replikasi, tidak ada masalah. Dengan replikasi, ada tiga sumber anomali:
- Replication lag β follower belum menerima log dari leader.
- Concurrent writes β dua node menerima write berbeda untuk key sama.
- Network partition β node tak bisa saling bicara tapi tetap melayani klien.
Consistency model = kontrak antara sistem dan klien tentang nilai apa yang mungkin dikembalikan. Bukan jaminan "data benar", tapi jaminan "data yang kamu lihat termasuk himpunan hasil yang legal".
Empat level praktis yang perlu dihafal
Dari kuat ke lemah (bukan tangga linear β ini bukan skala satu dimensi, lihat bagian miskonsepsi):
| Model | Jaminan | Biaya |
|---|---|---|
| Linearizability (strong) | Setiap operasi tampak atomik & instan pada satu titik waktu global. Real-time ordering dihormati. | Koordinasi lintas node per-operasi. Latency β slowest quorum member. Tidak tersedia saat partition. |
| Sequential consistency | Semua node melihat urutan operasi yang sama, tapi urutan itu tidak harus sesuai waktu real. | Butuh total order; tanpa batasan real-time. |
| Causal consistency | Operasi yang berhubungan sebab-akibat dilihat semua node dalam urutan sama. Operasi konkuren boleh beda urutan. | Butuh tracking dependency (vector clock / versi). Tersedia saat partition. |
| Eventual consistency | Kalau tidak ada write baru, semua replica akhirnya konvergen. Tanpa batas waktu. | Paling murah, paling tersedia. Klien bisa lihat apa saja di jendela antara. |
Read-your-writes (RYW) bukan level, tapi session guarantee β property yang dijamin untuk satu klien sepanjang sesinya. Klien yang menulis nilai X akan selalu membaca X atau lebih baru. Ini biasanya yang sebenarnya dibutuhkan user, dan jauh lebih murah daripada linearizability.
Session guarantees lain yang sering satu paket (dari sesi Vogels 2009, "Eventually Consistent", CACM):
- Monotonic reads β kamu tidak akan "mundur waktu" (baca versi lebih lama dari yang sudah kamu lihat).
- Monotonic writes β write dari klien sama diterapkan berurutan.
- Writes-follow-reads β write setelah membaca versi X didasarkan pada X atau lebih baru.
βοΈ Cara Kerja Internal
1. Bagaimana "strong" diimplementasikan
Tiga mekanisme dominan:
(a) Single-leader + synchronous replication. Semua write ke leader; leader tunggu ACK dari follower (atau quorum) sebelum balas klien. Read dari leader = linearizable. Ini model PostgreSQL synchronous_commit=on dengan synchronous_standby_names, MySQL semi-sync.
Catatan penting: read dari follower tetap tidak linearizable meski replikasi sinkron, karena follower bisa saja belum apply β kecuali replikasi sinkron berarti "apply", bukan "received WAL". Di PostgreSQL, synchronous_commit=remote_apply yang memberi jaminan itu; remote_write hanya jaminan sampai OS follower, bukan apply.
(b) Quorum (R + W > N). Cassandra/Dynamo-style. Kalau W + R > N, read set dan write set pasti beririsan β read melihat write terakhir. Contoh N=3, W=2, R=2 β 2+2 > 3 β.
Tapi ada lubang besar: quorum memberi strong-ish, bukan linearizable, karena tidak ada jaminan urutan antar-quorum (dua write konkuren bisa "menang" di quorum berbeda β ini yang disebut quorum inconsistency / "sloppy quorum"). Cassandra menyebut level QUORUM/LOCAL_QUORUM; klaim linearizable-nya butuh lightweight transactions (Paxos) atau leader-only read.
(c) Konsensus (Raft/Paxos) + clock uncertainty. etcd, Consul, CockroachDB, Spanner. Spanner menambahkan TrueTime: interval ketidakpastian jam (Ξ΅, orde milidetik β angka pastinya bergantung deployment dan commit-wait, jangan hafal satu angka) dan commit-wait supaya timestamp commit pasti sudah lewat di semua node. Ini yang membuatnya externally consistent (linearizable) lintas region β dengan biaya write latency = 2Ξ΅ + round-trip.
2. Bagaimana "eventual" diimplementasikan & kenapa bisa konvergen
Konvergensi butuh aturan resolusi konflik yang deterministik dan komutatif/idempoten:
- Last-Write-Wins (LWW) β pakai timestamp fisik. Sederhana, tapi membuang write dan bergantung pada clock skew. Ini penyebab paling umum data hilang diam-diam di sistem AP.
- Version vectors / dotted version vectors β deteksi konflik, serahkan resolusi ke aplikasi (Riak siblings).
- CRDT β struktur yang merge-nya komutatif & asosiatif secara matematis: G-Counter, OR-Set, LWW-Register, RGA untuk text. Konvergen tanpa koordinasi. Dipakai di Figma (sebelumnya), Automerge, Yjs, Redis CRDT (enterprise).
- Anti-entropy β Merkle tree untuk membandingkan range dan memperbaiki divergence (Dynamo paper, 2007).
3. Bagaimana RYW diimplementasikan (murah, ini kuncinya)
Empat teknik, dari paling murah:
- Sticky routing / session affinity β klien selalu ke node yang sama. Murah, rapuh (node mati = jaminan hilang).
- Read-from-leader setelah write β tandai sesi "kotor" selama T detik; semua read ke leader. Implementasi di aplikasi, bukan DB.
- Version/lag token β server balas
X-Min-Version: 41235; klien kirim balik; replica nolak read kalau belum sampai versi itu (atau tunggu sebentar). Ini yang dipakai banyak sistem: read-your-writes tanpa memaksa semua read ke leader. - Monotonic read via sticky timestamp β klien bawa timestamp read terakhir, replica yang lebih muda menolak.
Teknik 3 dan 4 memberi RYW dengan biaya mendekati nol di steady state β inilah kenapa "konsisten" jarang butuh full linearizability.
ποΈ Diagram Alur
Write path (leader + async replica, RYW via token):
client ββwriteβββΊ leader ββACK v=101βββΊ client
β
βββasync logβββΊ replica (v=99 saat ini)
client ββread (+ header "min-version: 101")βββΊ replica
β
βββββββββββββββββββββββββ΄βββββββββββ
v>=101? v<101?
β β
serve read wait β€ N ms, else
βββΊ route ke leader
atau return 503/retry
Quorum overlap (N=3, W=2, R=2):
W set {A,B} R set {B,C}
β β
βββββ B ββββββββββ β irisan menjamin read melihat write
Partition (CAP dalam praktik, N=3):
[A,B] βββββ [C]
β β
mayoritas minoritas
tetap write ? β CP: tolak write
β AP: terima write, konflik nanti
βοΈ Trade-off & Kapan JANGAN Dipakai
Ini bagian yang menentukan senior vs junior.
Kapan JANGAN pakai strong consistency (linearizability)
- Read-heavy, write-light, toleran staleness: feed sosial, katalog produk, profil user, rekomendasi. 50ms staleness tidak terlihat; 50ms tambahan latency terlihat di setiap request.
- Cross-region read: linearizable read lintas benua = round-trip ke leader. EU user baca data dari us-east-1 = +100ms p50, dan gagal total saat link transatlantik putus. Untuk 95% halaman, ini pemborosan.
- Skala write tinggi: koordinasi per-write membatasi throughput pada kapasitas node tunggal/quorum. Ini alasan DynamoDB menawarkan eventually consistent read (biaya RCU setengah dari strongly consistent) β hemat 50% biaya baca untuk data yang tidak butuh.
- Saat availability adalah requirement bisnis nyata: kalau sistem harus tetap menerima order saat partition, kamu tidak bisa CP. Pilih AP + rekonsiliasi bisnis (kredit limit, reservasi stok dengan kompensasi).
Kapan WAJIB strong(ish)
- Uang, stok, kuota, hak akses, idempotency key, uniqueness constraint. Bukan karena "data penting" β karena anomali di sini menciptakan kerugian yang tidak bisa direkonsiliasi otomatis. Double-spend tidak bisa "di-merge".
- Read-after-write di alur user yang sama (submit form β lihat hasil). Minimal RYW, tidak perlu linearizable.
- Distributed lock / leader election. Butuh konsensus, titik.
Biaya yang sering dilupakan
| Pilihan | Latency | Availability | Kompleksitas aplikasi |
|---|---|---|---|
| Linearizable | Tinggi (quorum/leader RTT) | Turun saat partition (CP) | Rendah β "anggap satu DB" |
| Causal | Sedang | Tinggi | Sedang β butuh dependency tracking |
| Eventual | Rendah | Tinggi | Tinggi β resolusi konflik, UI yang toleran, kompensasi |
| Eventual + RYW | Rendah | Tinggi | Rendah-sedang β ini sweet spot paling umum |
Poin yang sering luput: eventual consistency tidak menghilangkan biaya, ia memindahkannya ke aplikasi dan ke user. UI yang menampilkan "komentar kamu sedang diproses", job rekonsiliasi, tim customer service yang memperbaiki data. Itu biaya nyata, sering lebih besar dari biaya latency.
Jebakan "sesuaikan level per operasi" tanpa disiplin
Mencampur level di satu sistem membuat invariant tidak jelas. Kalau saldo dibaca eventually-consistent tapi dipakai untuk keputusan write strongly-consistent, kamu punya race. Aturan praktis: tentukan invariant dulu, lalu turunkan level per operasi β jangan sebaliknya.
π Contoh Produksi & Failure Mode
1. Read-your-writes hilang saat failover (pola paling umum). User update profil β write ke leader β leader mati β replica promosi β replika promosi tertinggal beberapa detik β user refresh, melihat data lama, submit ulang. Muncul sebagai "data saya kembali sendiri". Mitigasi: version token, atau read-from-leader sampai replikasi terkonfirmasi.
2. LWW membuang update (silent data loss). Dua device update field berbeda di record sama; timestamp device lebih cepat menang; field lain hilang. Ini failure mode klasik sync mobile. Mitigasi: per-field LWW (bukan per-record), atau CRDT map.
3. Quorum "cukup" tapi tidak linearizable.
Cassandra QUORUM read + write pada dua write konkuren bisa menghasilkan kedua klien sama-sama "berhasil" dengan nilai berbeda. Bukan bug β itu definisi quorum tanpa consensus. Kalau butuh compare-and-set, pakai LWT (Paxos) dan terima biayanya (4 round-trip).
4. Stale read menyebabkan write invalid.
Baca kuota dari replica (masih 1 tersisa), dua request paralel sama-sama lolos cek, kuota jadi -1. Ini bukan masalah konsistensi database saja β ini check-then-act lintas replica. Solusi: atomic conditional write di leader (UPDATE ... WHERE kuota > 0), bukan baca-lalu-tulis.
5. Monotonic reads dilanggar oleh load balancer. Round-robin ke 3 replica dengan lag berbeda β user melihat komentar, refresh, komentar hilang, refresh, muncul lagi. Terlihat seperti "ghost data". Mitigasi: sticky per-sesi atau lag-aware routing.
6. Split-brain dengan dua leader (bukan CP, bukan AP β rusak).
Failover prematur / unhealthy threshold terlalu agresif β dua node sama-sama menganggap diri leader β divergen write. Redis Sentinel, Patroni, dan MySQL Group Replication semuanya pernah punya insiden kelas ini. Ini alasan fencing token / STONITH ada.
β οΈ Miskonsepsi Umum
-
"CAP = pilih 2 dari 3." Salah secara operasional. P adalah kondisi yang terjadi, bukan pilihan. Saat partition, kamu memilih C atau A per operasi. Saat tidak ada partition, kamu memilih latency vs consistency β itulah yang PACELC tambahkan (If Partition then A or C, Else L or C). Model mental yang lebih akurat: CAP adalah tentang perilaku saat gagal, PACELC tentang biaya saat normal.
-
"Eventual consistency = tidak konsisten / tidak ada jaminan." Eventual adalah jaminan nyata (konvergensi, asalkan tidak ada write baru dan anti-entropy jalan). Yang tidak dijamin adalah kapan dan urutan antara. Sistem yang mendesain eventual dengan CRDT punya jaminan lebih kuat dari "mungkin benar" β mereka punya strong eventual consistency (konvergen deterministik).
-
"Konsisten = benar." Consistency model tidak menjamin data sesuai realitas, hanya jaminan tentang apa yang mungkin dilihat. Bug aplikasi yang menulis nilai salah tetap konsisten sempurna.
-
"Pakai strongly consistent di semua tempat itu aman." Aman secara kebenaran, tapi menciptakan coupling: satu region down = semua read down. Banyak outage besar adalah hasil terlalu banyak strong read, bukan terlalu sedikit.
-
"Cache bukan bagian dari masalah konsistensi." Cache adalah replica. Semua model konsistensi berlaku untuk Redis dan CDN juga β cache invalidation adalah masalah konsistensi terdistribusi dengan nama lain.
-
"Kalau pakai Postgres, semua otomatis konsisten." Read replica, connection pooler dengan routing, dan multi-region read semua memperkenalkan jaminan yang lebih lemah.
synchronous_commit=off+ read replica = eventual, walau DB-nya "SQL".
β Cek Pemahaman
- Sistem pakai N=3, W=1, R=3. Apakah read linearizable? Apakah write durable? Apa yang terjadi kalau node yang menerima write mati sebelum replikasi?
- Kamu punya endpoint
POST /orderslaluGET /orders/:id. Desain mekanisme RYW minimum yang tidak memaksa semuaGETke leader. Versi mana yang kamu kirim ke klien, dan apa yang terjadi kalau replica tersebut mati? - Tim minta "eventual consistency supaya cepat" untuk fitur saldo e-wallet. Sebutkan dua invariant yang rusak, dan skema apa yang sebenarnya kamu usulkan.
π Poin Kunci
- Consistency model adalah kontrak tentang himpunan hasil legal, bukan jaminan kualitas data.
- Linearizability mahal karena butuh koordinasi per-operasi; itu sumber latency dan titik kegagalan, bukan sifat bawaan "database bagus".
- Read-your-writes menutupi 80% kebutuhan nyata dengan biaya mendekati nol β pakai version token, bukan read-from-leader untuk semua.
- Quorum β linearizable.
W+R>Nmencegah read melewatkan write yang sudah ACK, tapi tidak memberi total order. - Eventual consistency memindahkan biaya ke aplikasi: resolusi konflik, UI toleran, rekonsiliasi. Hitung biaya itu sebelum memilih.
- LWW = potensi kehilangan write. Gunakan per-field atau CRDT kalau data tidak boleh hilang.
- CAP hanya bicara saat partition; PACELC bicara biaya sehari-hari. Keputusan arsitektur nyata didominasi PACELC.
- Cache dan read replica adalah replica. Aturan mainnya sama.
β‘οΈ Eksplorasi Lanjut
- Baca ulang paper asli: Vogels, "Eventually Consistent" (CACM 2009) untuk session guarantees; DeCandia et al., "Dynamo" (SOSP 2007) untuk vector clock & anti-entropy; Corbett et al., "Spanner" (OSDI 2012) untuk TrueTime & commit-wait.
- Jepsen (jepsen.io) β baca 2β3 analisis sistem yang kamu pakai (PostgreSQL, MongoDB, Redis, etcd). Ini cara tercepat membangun intuisi "klaim vs kenyataan".
- Kleppmann, DDIA bab 5 & 9 β bab 5 untuk fondasi replikasi, bab 9 untuk linearizability dan kenapa ia mahal.
- Topik berikutnya yang natural setelah ini: consistency + idempotency di API (retry yang aman) dan saga/outbox pattern β keduanya adalah konsekuensi langsung dari memilih eventual di batas service.
- Latihan konkret: ambil satu endpoint di sistemmu, tuliskan invariant-nya, lalu untuk tiap read tentukan level minimum yang dibutuhkan. Kalau hasilnya "semua butuh strong", curigai analisisnya, bukan sistemnya.