Chapter sebelumnya meninggalkan kita pada kondisi replika yang bisa berbeda isi sesaat: leader sudah menyimpan tulisan baru sementara follower masih tertinggal. Pertanyaannya, jawaban apa yang boleh diberikan sistem kepada pembaca dalam kondisi seperti itu? Consistency model adalah kontrak yang menjawabnya: aturan tentang urutan dan kesegaran bacaan yang dijanjikan sistem kepada klien. Memilih model yang tepat menentukan apakah dashboard analitik cukup memakai eventual consistency yang murah, atau justru membutuhkan jaminan lebih kuat dengan harga latensi dan ketersediaan.

Konsistensi sebagai Kontrak #

Consistency model bukan tentang seberapa cepat data tersalin, melainkan tentang janji mana yang boleh dipegang klien. Model terkuat, strong consistency (linearizability), menjanjikan setiap baca melihat tulisan terbaru seolah data hanya punya satu salinan — janji yang menuntut koordinasi antar node pada setiap operasi sehingga mahal dan lambat. Model terlemah, eventual consistency, hanya menjanjikan replika akan sama setelah tulisan berhenti dan lag tertutup; bacaan boleh dilayani replika mana pun sehingga cepat dan tahan gangguan. Di antara keduanya ada session guarantees: janji yang berlaku per klien, jauh lebih murah dipenuhi karena tidak menuntut koordinasi global.

Model konsistensiJanji kepada klienContoh pemakaian umum
Strong (linearizable)Setiap baca melihat tulisan terbaru, seolah satu salinanKoordinasi antar node, lock, saldo pembayaran
Read-your-writesKlien selalu melihat tulisannya sendiriHalaman profil setelah menyunting
Monotonic readsBacaan satu klien tidak pernah mundur ke keadaan lamaLeaderboard, feed pesan
EventualReplika akan menyamai diri setelah tulisan berhentiCache, CDN, dashboard analitik

Urutan pada tabel di atas juga urutan harganya. Strong consistency menuntut setiap operasi berkoordinasi, misalnya baca yang menunggu mayoritas replika menjawab, sehingga satu replika lambat langsung memperlambat semua orang. Eventual consistency membiarkan setiap replika menjawab sendiri, akibatnya sistem tetap cepat dan tetap melayani walau sebagian replika terputus. Kebanyakan platform data memilih eventual sebagai default lalu menambahkan session guarantees pada fitur yang membutuhkannya, karena janji per klien hampir gratis dibandingkan koordinasi global.

Anomali Eventual Consistency dan Perbaikannya #

Eventual consistency terasa mulus sampai kita menemui anomali. Simulasi berikut memakai dua replica sederhana — masing-masing menyimpan skor beserta nomor versi tulisan terakhir — untuk memperlihatkan dua anomali klasik dan cara session guarantees menutupnya. Replica-A menerima seluruh tulisan; replica-B tertinggal dua versi, meniru follower yang sedang mengalami lag.

python
"""Anomali baca pada dua replica dan perbaikannya dengan session guarantees."""


class Replica:
    """Satu replica menyimpan skor beserta nomor versi tulisan terakhir."""

    def __init__(self, nama):
        self.nama = nama
        self.versi = 0
        self.skor = 0

    def tulis(self, skor):
        self.versi += 1
        self.skor = skor
        return self.versi

    def baca(self):
        return self.skor, self.versi


replica_a = Replica("replica-A")  # menerima seluruh tulisan
replica_b = Replica("replica-B")  # menerima tulisan dengan jeda (lag)

print("[1] Empat tulisan masuk ke replica-A; replica-B baru menerima dua")
for skor in (100, 200, 300, 400):
    replica_a.tulis(skor)
replica_b.tulis(100)
replica_b.tulis(200)
print(f"    replica-A: skor={replica_a.skor} (versi {replica_a.versi})")
print(f"    replica-B: skor={replica_b.skor} (versi {replica_b.versi})")

print("[2] Klien membaca bergantian A, B, A, B (tanpa jaminan sesi)")
for i in range(4):
    replica = (replica_a, replica_b)[i % 2]
    skor, versi = replica.baca()
    print(f"    baca#{i + 1} dari {replica.nama}: skor={skor} (versi {versi})")
print("    -> skor turun dari 400 ke 200: pelanggaran monotonic reads")

print("[3] Klien dengan jaminan monotonic reads (mengingat versi terakhir dilihat)")
versi_terakhir = 0
for i in range(4):
    replica = (replica_a, replica_b)[i % 2]
    skor, versi = replica.baca()
    if versi < versi_terakhir:
        print(f"    baca#{i + 1} dari {replica.nama}: versi {versi} < {versi_terakhir}, "
              f"tolak dan baca ulang")
        skor, versi = replica_a.baca()
    versi_terakhir = max(versi_terakhir, versi)
    print(f"    baca#{i + 1} menghasilkan skor={skor} (versi {versi})")

print("[4] Read-your-writes: penulis membaca segera setelah menulis")
versi_tulis = replica_a.tulis(500)  # penulis menulis ke replica-A
print(f"    penulis menulis skor=500 ke replica-A (versi {versi_tulis})")
skor, versi = replica_b.baca()  # load balancer mengarahkan baca ke replica-B
print(f"    baca dari replica-B: skor={skor} -> tidak melihat tulisannya sendiri")
skor, versi = replica_a.baca()  # rutekan baca pasca-tulis ke sumber tulisan
print(f"    baca dari replica-A: skor={skor} -> read-your-writes terpenuhi")

Output yang Dihasilkan:

text
[1] Empat tulisan masuk ke replica-A; replica-B baru menerima dua
    replica-A: skor=400 (versi 4)
    replica-B: skor=200 (versi 2)
[2] Klien membaca bergantian A, B, A, B (tanpa jaminan sesi)
    baca#1 dari replica-A: skor=400 (versi 4)
    baca#2 dari replica-B: skor=200 (versi 2)
    baca#3 dari replica-A: skor=400 (versi 4)
    baca#4 dari replica-B: skor=200 (versi 2)
    -> skor turun dari 400 ke 200: pelanggaran monotonic reads
[3] Klien dengan jaminan monotonic reads (mengingat versi terakhir dilihat)
    baca#1 menghasilkan skor=400 (versi 4)
    baca#2 dari replica-B: versi 2 < 4, tolak dan baca ulang
    baca#2 menghasilkan skor=400 (versi 4)
    baca#3 menghasilkan skor=400 (versi 4)
    baca#4 dari replica-B: versi 2 < 4, tolak dan baca ulang
    baca#4 menghasilkan skor=400 (versi 4)
[4] Read-your-writes: penulis membaca segera setelah menulis
    penulis menulis skor=500 ke replica-A (versi 5)
    baca dari replica-B: skor=200 -> tidak melihat tulisannya sendiri
    baca dari replica-A: skor=500 -> read-your-writes terpenuhi

Mari kita telaah baris-baris kode di atas:

  1. Baris 12-18 (tulis dan baca): setiap tulisan menaikkan nomor versi, dan setiap baca mengembalikan skor beserta versinya — versi inilah alat ukur kesegaran jawaban.
  2. Baris 24-30 (menyiapkan lag): empat tulisan masuk ke replica-A sedangkan replica-B hanya dua, meniru replication lag dari chapter sebelumnya.
  3. Baris 32-37 (anomali pertama): klien yang dibebaskan memilih replica bergantian melihat skor 400 lalu 200 — data terlihat mundur karena pelanggaran monotonic reads.
  4. Baris 39-49 (perbaikan pertama): klien mengingat versi tertinggi yang pernah dilihat; jawaban yang lebih tua ditolak dan dibaca ulang dari replica-A, sehingga skor tidak pernah mundur.
  5. Baris 51-57 (anomali kedua dan perbaikannya): penulis menulis ke replica-A lalu membaca dari replica-B yang basi; dengan read-your-writes, baca sesaat setelah tulis cukup dirutekan ke sumber tulisan.

Membaca Hasil: Jaminan Murah Tanpa Koordinasi Global #

Kedua perbaikan di atas tidak menyingkirkan lag — replica-B tetap tertinggal sampai akhir simulasi. Yang berubah adalah pengalaman satu klien: skor tidak pernah mundur, dan penulis selalu melihat tulisannya sendiri. Session guarantees memang murah karena hanya menyimpan sedikit keadaan per klien (versi terakhir, sumber tulisan terakhir), tanpa memaksa seluruh replika menyinkronkan sebelum menjawab. Bacaan yang lebih kuat tetap tersedia dengan harga lebih: quorum read menunggu jawaban dari mayoritas replika (rumus praktisnya R + W > N), sehingga satu replika lambat langsung memperlambat semua orang.

Praktik yang sehat adalah menetapkan model konsistensi per fitur, bukan per sistem. Leaderboard cukup monotonic reads karena urutan skor yang mundur terasa salah bagi pemain. Halaman profil butuh read-your-writes karena pengguna yang menyimpan suntingan lalu tidak melihat perubahannya akan menganggap aplikasi rusak. Proses checkout pembayaran layak memakai jalur strong consistency karena menjual barang berdasarkan saldo basi berbahaya bagi bisnis. Satu platform bisa melayani ketiganya sekaligus selama tiap fitur eksplisit memilih kontraknya.

Konsistensi menjawab pertanyaan bentuk data saat komponen sehat. Pertanyaan berikutnya adalah kelangsungan layanan saat komponen berhenti: bagaimana sistem tetap bekerja dan pulih dengan sendirinya? Itulah pembahasan Chapter Fault Tolerance.