Data Loss vs Availability Trade-off #

Dalam dunia sistem terdistribusi, salah satu hukum alam paling mendasar yang harus kita hadapi adalah Teorema CAP (Consistency, Availability, Partition Tolerance). Teorema ini menyatakan bahwa ketika terjadi pembagian jaringan (network partition), suatu sistem hanya dapat memilih antara mempertahankan konsistensi data (consistency) atau ketersediaan layanan (availability). Di Apache Kafka, manifestasi paling nyata dari trade-off ini terletak pada bagaimana kita mengonfigurasi penanganan kegagalan ketika pemimpin partisi (leader) mati mendadak, sementara replika pengikut (follower) yang tersisa tidak berada dalam status sinkron (out-of-sync). Keputusan arsitektural ini akan menentukan apakah sistem kita akan memilih kehilangan data demi menjaga kelangsungan operasional, atau memilih mati sementara demi mengamankan setiap bit data.


Memahami Kategori Pengikut: In-Sync vs Out-of-Sync #

Sebelum membahas trade-off arsitektural secara mendalam, kita harus terlebih dahulu memperjelas bagaimana Kafka membedakan status replika pengikut.

1. In-Sync Replicas (ISR) #

ISR adalah kelompok broker pengikut yang aktif berkomunikasi dengan leader dan berhasil menyalin data dengan selisih waktu yang sangat minim. Status keaktifan ini diatur oleh parameter:

$$\text{replica.lag.time.max.ms}$$

Jika sebuah follower berhasil mengirimkan permintaan tarik data (fetch request) dan tidak tertinggal lebih dari durasi waktu tersebut (default: 30 detik), maka follower tersebut diakui sebagai anggota ISR yang sah. Anggota ISR dinilai memiliki data yang identik dengan leader.

2. Out-of-Sync Replicas (OSR) #

Jika sebuah follower mengalami kegagalan proses, crash fisik, atau mengalami latensi jaringan yang sangat parah sehingga ia gagal mengirimkan fetch request dalam batas waktu replica.lag.time.max.ms, broker tersebut akan didepak dari keanggotaan ISR dan masuk ke status out-of-sync. Broker dalam OSR dinilai memegang data yang usang (stale) dan tertinggal di belakang leader.

flowchart TD
    subgraph Kluster ["Kluster Kafka (Partisi 0)"]
        direction TB
        L0["Broker 1 (Leader) <br> LEO: 100"]
        F1["Broker 2 (Follower - ISR) <br> LEO: 100 <br> Fetch: 1s ago"]
        F2["Broker 3 (Follower - Out-of-Sync) <br> LEO: 80 <br> Fetch: 45s ago"]
    end

    L0 -. Replikasi .-> F1
    L0 -. Replikasi (Gagal/Lambat) .-> F2

    style L0 stroke:#0288d1,stroke-width:2px
    style F1 stroke:#2e7d32,stroke-width:2px
    style F2 stroke:#c62828,stroke-width:2px

Skenario Krisis Failover #

Bencana arsitektural terjadi ketika Broker 1 (Leader) mati mendadak, dan pada saat yang bersamaan, satu-satunya follower yang tersisa yang masih hidup adalah Broker 3 (yang berstatus Out-of-Sync).

Di sinilah Kafka bertanya kepada arsitek sistem melalui konfigurasi: “Apakah kita mengizinkan Broker 3 menjadi Leader baru?”


Membedah Parameter unclean.leader.election.enable #

Parameter konfigurasi unclean.leader.election.enable adalah sakelar utama yang mengontrol perilaku pemilihan pemimpin dalam kondisi krisis di atas. Konfigurasi ini dapat diatur di tingkat broker (server.properties) atau ditimpa di tingkat topik secara dinamis.

1. Opsi unclean.leader.election.enable = true (Mengutamakan Availability) #

Ketika kita menyetel opsi ini ke true, kita secara sadar mengizinkan replika yang tidak sinkron (out-of-sync replicas) untuk dipilih menjadi pemimpin baru partisi jika tidak ada satu pun anggota ISR asli yang tersisa hidup.

Keuntungan: Ketersediaan Tinggi (Availability) #

Sistem menulis (write) dan membaca (read) klien akan terus berjalan. Partisi tidak akan mengalami downtime yang lama karena segera setelah ada satu broker pengikut yang hidup (meskipun datanya tertinggal jauh), broker tersebut akan langsung dipromosikan menjadi leader baru untuk melayani request dari produser dan konsumen.

Risiko Fatal: Kehilangan Data Mutlak & Kerusakan Offset #

Jika Broker 3 (yang data LEO-nya tertinggal di offset 80) diangkat menjadi leader baru padahal leader lama sudah menulis hingga offset 100:

  1. Pesan Hilang: Pesan dari offset 81 hingga 100 yang sempat ditulis di leader lama akan hilang selamanya dari kluster.
  2. Pemotongan Log (Log Truncation): Ketika Broker 1 (leader lama) kembali menyala, ia akan mendaftar kembali sebagai pengikut. Broker 1 akan melihat bahwa leader baru hanya memiliki data hingga offset 80. Untuk menyelaraskan diri, Broker 1 terpaksa memotong log fisiknya sendiri dari offset 81 ke 100.
  3. Pesan Baru Menimpa Offset Lama: Jika produser mengirimkan pesan baru ke leader baru (Broker 3), pesan baru tersebut akan diberi offset 81, 82, dst. Pesan baru ini menimpa rentang offset lama dengan konten data yang sama sekali berbeda. Hal ini akan sangat membingungkan aplikasi konsumen yang melacak data secara sekuensial.
flowchart TD
    subgraph Kondisi_Awal ["1. Sebelum Leader Crash"]
        direction TB
        L_A["Leader Lama (Broker 1) <br> [Offsets: 0 - 100]"]
        F_A["Follower Out-of-Sync (Broker 2) <br> [Offsets: 0 - 80]"]
    end

    subgraph Crash_Event ["2. Leader Crash & Unclean Election = true"]
        direction TB
        L_Dead["Broker 1 (Mati)"]
        F_Promoted["Broker 2 (Promosi Jadi Leader Baru) <br> [Offsets: 0 - 80]"]
        Note_A["Offsets 81-100 Hilang dari Kluster!"]
    end

    subgraph Recovery_Event ["3. Leader Lama Kembali Hidup (Sebagai Follower)"]
        direction TB
        F_New["Broker 1 (Kembali Hidup) <br> [Log Dipotong ke Offset 80]"]
        L_New["Broker 2 (Leader Baru) <br> [Menerima Pesan Baru di Offset 81]"]
    end

2. Opsi unclean.leader.election.enable = false (Mengutamakan Durabilitas/Konsistensi) #

Ini adalah nilai bawaan (default) di Apache Kafka modern. Ketika terjadi skenario krisis di atas, Kafka akan melarang keras pemilihan pemimpin dari replika yang tidak sinkron.

Keuntungan: Konsistensi & Durabilitas Data Mutlak #

Kafka menjamin tidak akan ada pesan yang hilang secara diam-diam akibat kegagalan sinkronisasi, dan linearitas offset data akan tetap terjaga secara sempurna. Tidak akan ada pemotongan log paksa pada broker leader lama ketika ia kembali pulih.

Risiko: Downtime Layanan (Unavailability) #

Selama Broker 1 (atau anggota ISR asli lainnya) belum kembali menyala, partisi tersebut akan dinyatakan OFFLINE. Klien produser yang mencoba menulis ke partisi ini akan menerima error LeaderNotAvailableException. Konsumen yang mencoba membaca dari partisi ini juga tidak akan mendapat data baru. Layanan bisnis kita untuk partisi tersebut lumpuh total selama masa pemulihan broker.


Peran Leader Epoch dalam Rekonsiliasi Log #

Sebelum versi 0.11, Kafka mengandalkan High Watermark (HW) untuk melakukan pemotongan log saat terjadi failover. Metode ini memiliki cacat arsitektural yang dapat menyebabkan kehilangan data atau duplikasi tidak teratur bahkan dengan unclean.leader.election.enable=false.

Sejak versi 0.11, Kafka memperkenalkan konsep Leader Epoch. Leader Epoch adalah sebuah angka bilangan bulat (sequence counter) yang melacak berapa kali pemimpin partisi telah berganti sejak partisi tersebut pertama kali dibuat.

Bagaimana Leader Epoch Melindungi Integritas Log? #

Setiap kali broker pemimpin baru terpilih, Controller akan menaikkan nilai Leader Epoch. Pemimpin baru tersebut mencatat offset pertamanya bersama dengan nomor epoch barunya di dalam file leader-epoch-checkpoint lokal.

Ketika broker lama (misalnya Broker 1 yang tadinya mati) kembali hidup sebagai follower:

  1. Broker 1 tidak langsung memotong log-nya hingga High Watermark lokalnya.
  2. Broker 1 mengirimkan permintaan khusus bernama OffsetsForLeaderEpochRequest ke leader aktif saat ini (Broker 2).
  3. Broker 2 membalas dengan batas offset akhir untuk epoch yang ditanyakan.
  4. Berdasarkan respon tersebut, Broker 1 dapat menentukan dengan tepat pada offset berapa datanya menyimpang dari leader aktif saat ini, lalu memotong log-nya pada batas tersebut saja.

Mekanisme ini mencegah skenario di bawah High Watermark yang menyimpang dan menjamin bahwa replika follower tidak akan pernah memotong data yang sebenarnya sudah terkonfirmasi aman oleh kuorum kluster.


Mekanisme Pemilihan Pemimpin (Election) di Controller: ZooKeeper vs KRaft #

Proses pemilihan pemimpin yang dinamis dikelola oleh broker khusus yang bertindak sebagai Controller kluster. Cara Controller mengeksekusi pemilihan ini berkembang pesat seiring transisi arsitektur Kafka.

Era ZooKeeper #

  1. Ketika sebuah broker leader mati, ZooKeeper mendeteksi hilangnya sesi ephemeral broker tersebut.
  2. ZooKeeper mengirimkan notifikasi (watch trigger) ke Controller.
  3. Controller membaca daftar keanggotaan partisi dan ISR dari ZooKeeper node /brokers/topics/[topic]/partitions/[partition]/state.
  4. Jika unclean.leader.election.enable=false, Controller menyaring daftar broker yang hidup dan memilih broker pertama yang ada di dalam ISR. Jika tidak ada broker ISR yang hidup, partisi dinobatkan sebagai offline.
  5. Controller memperbarui state baru ke ZooKeeper dan mendistribusikan data baru ini ke seluruh broker kluster menggunakan perintah LeaderAndIsrRequest.

Era KRaft (Modern) #

Di era KRaft, proses ini jauh lebih efisien karena Controller aktif menyimpan salinan metadata langsung di memori (Active Controller Metadata Image).

  1. Kegagalan broker dideteksi langsung melalui hilangnya detak jantung (heartbeat) broker ke kuorum KRaft Controller.
  2. Pemimpin KRaft Controller langsung menghitung state baru untuk partisi tersebut.
  3. Controller menuliskan record perubahan kepemimpinan partisi (PartitionRecord) ke log metadata internal @metadata.
  4. Seluruh broker di kluster memperbarui state lokal mereka secara instan dengan membaca stream metadata delta dari log tersebut secara asinkron. Hal ini mengeliminasi latensi komunikasi jaringan serial yang lambat di era ZooKeeper.

Analisis Komparasi Kualitatif #

Tabel berikut merangkum perbedaan esensial dari kedua opsi konfigurasi di atas untuk membantu kita merumuskan kebijakan operasional kluster:

Parameter Evaluasi unclean.leader.election.enable = true unclean.leader.election.enable = false
Fokus Utama Ketersediaan Sistem (Availability) Durabilitas & Konsistensi Data (Consistency)
Perilaku Failover Izinkan out-of-sync follower menjadi leader baru Blokir pemilihan leader baru hingga ISR pulih
Potensi Kehilangan Data Sangat Tinggi (Semua offset yang belum ter-replikasi hilang) Nol (Pesan diamankan di log disk broker mati)
Resiko Log Truncation Ya (Follower lama memotong data historis miliknya) Tidak (Log tetap utuh menunggu broker hidup kembali)
Dampak Pada Klien Sukses menulis/membaca dengan risiko anomali data Penulisan ditolak dengan LeaderNotAvailableException
Use Case Terbaik Streaming video, IoT telemetri, Clickstream tracking Transaksi Bank, Inventory stock, Order payment

Studi Kasus Nyata: Dampak Listrik Padam di Pusat Data (Data Center Power Outage) #

Mari kita bedah skenario kegagalan operasional yang umum terjadi di industri untuk melihat bagaimana kedua pilihan konfigurasi ini berdampak pada integritas database hilir (downstream database).

Latar Belakang Masalah #

Sebuah kluster Kafka dengan 3 broker mengalami insiden kegagalan daya listrik pada rak server utama yang menampung Broker 1 (Leader) dan Broker 2 (Follower/ISR). Broker 3 (Follower) berada di rak sekunder yang mendapatkan daya cadangan dari genset sehingga tetap hidup. Namun, sebelum listrik padam, koneksi jaringan antara rak utama dan rak sekunder sempat terganggu selama 10 menit, menyebabkan Broker 3 berstatus out-of-sync (ketinggalan 50.000 pesan).

Jalur Kejadian A: Menggunakan unclean.leader.election.enable=true #

  1. Broker 1 and Broker 2 mati secara mendasar karena pemadaman listrik.
  2. Kluster mendeteksi Broker 3 adalah satu-satunya broker yang hidup. Karena opsi diset true, Broker 3 diangkat menjadi Leader baru.
  3. Klien produser mengirimkan pesan transaksi baru (misalnya, ID Transaksi 900.000). Pesan ini ditulis di Broker 3 pada offset 150.000.
  4. Dua jam kemudian, listrik di rak utama menyala kembali. Broker 1 dan Broker 2 hidup kembali dan mendaftar sebagai follower ke Broker 3.
  5. Broker 1 mendeteksi bahwa data transaksi miliknya sendiri pada offset 150.000 hingga 200.000 (yang berisi transaksi ID 850.000 hingga 899.999) tidak ada di Broker 3.
  6. Broker 1 memotong log fisiknya dari offset 150.000 ke atas, menghapus 50.000 transaksi berharga.
  7. Aplikasi konsumen membaca dari Broker 3 dan menyalin data baru ke database SQL utama.
  8. Hasil Akhir: Database SQL kehilangan 50.000 transaksi secara permanen tanpa adanya log kesalahan di sisi produser. Terjadi selisih pembukuan keuangan senilai miliaran rupiah.

Jalur Kejadian B: Menggunakan unclean.leader.election.enable=false #

  1. Broker 1 dan Broker 2 mati akibat mati listrik.
  2. Broker 3 mendeteksi dirinya adalah satu-satunya broker yang hidup. Namun, karena opsi diset false, sistem melarang Broker 3 mempromosikan dirinya menjadi leader karena ia berstatus out-of-sync.
  3. Topik dinyatakan offline. Produser yang mencoba mengirim transaksi baru ditolak dengan error LeaderNotAvailableException. Aplikasi web menampilkan halaman “Sistem Sedang Mengalami Gangguan, Silakan Coba Beberapa Saat Lagi”.
  4. Dua jam kemudian, listrik menyala kembali. Broker 1 (Leader asli) hidup kembali.
  5. Kluster mendeteksi broker ISR asli telah pulih. Broker 1 kembali ditunjuk menjadi Leader aktif.
  6. Penulisan dibuka kembali. Antrean pesan di produser mulai dikirim kembali secara aman ke Broker 1.
  7. Hasil Akhir: Tidak ada satu pun transaksi yang hilang. Sistem mengalami downtime selama 2 jam, namun integritas data keuangan terjaga 100%.

Dampak pada Klien Produser: Penyanggaan Memori (Memory Buffering) #

Ketika kita memilih jalan aman dengan menyetel unclean.leader.election.enable=false, produser kita harus dikonfigurasi dengan toleransi kegagalan yang tepat untuk menangani masa downtime sementara dari partisi yang offline.

Saat partisi offline, produser tidak dapat mengirim data. Namun, produser asinkron Kafka tidak akan langsung crash. Ia memiliki area memori penyangga (memory buffer) untuk menampung pesan sementara.

Properti Penting Produser Saat Downtime Partisi #

  • buffer.memory: Ukuran memori maksimum (default: 33.554.432 byte / 32MB) yang dapat dialokasikan produser untuk menyangga record pesan yang belum terkirim ke broker.
  • max.block.ms: Durasi waktu maksimum (default: 60.000ms / 1 menit) di mana pemanggilan .send() akan diblokir ketika buffer memori produser telah penuh atau metadata partisi tidak tersedia. Jika batas waktu ini terlewati dan broker belum pulih, produser akan melempar TimeoutException.
  • retry.backoff.ms: Jeda waktu tunggu (default: 100ms) sebelum produser mencoba mengirimkan request yang gagal kembali ke broker leader.

Dengan memahami parameter ini, kita dapat menyelaraskan arsitektur klien agar mampu menyangga pesan di memori lokal secara aman selama masa kegagalan sementara (transient failure) tanpa membebani memori JVM aplikasi secara berlebihan.


Panduan Konfigurasi di Lapisan Produksi #

Untuk menerapkan pilihan kebijakan di atas, berikut adalah instruksi konfigurasi konkret yang dapat kita jalankan.

1. Konfigurasi Global Tingkat Kluster #

Penyetelan global dilakukan pada berkas config/server.properties (atau config/kraft/broker.properties untuk mode KRaft) di setiap broker kluster.

# JANGAN: Mengaktifkan ini secara global di kluster produksi multi-purpose
# unclean.leader.election.enable=true

# BENAR: Matikan secara default untuk menjaga durabilitas data secara global
unclean.leader.election.enable=false

2. Konfigurasi Spesifik Tingkat Topik (Dinamis) #

Sering kali, dalam satu kluster Kafka yang sama, kita memiliki topik yang membutuhkan durabilitas tinggi (misal: pembayaran) dan topik yang membutuhkan ketersediaan tinggi (misal: log debug). Kita bisa menimpa konfigurasi global di tingkat topik menggunakan tool CLI bawaan Kafka:

# Mengaktifkan Unclean Election secara kustom hanya untuk topik logging telemetri
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics \
  --entity-name telemetry-logs \
  --alter \
  --add-config unclean.leader.election.enable=true

# Memastikan topik transaksi keuangan tetap aman (unclean = false)
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics \
  --entity-name bank-transfers \
  --alter \
  --add-config unclean.leader.election.enable=false

# Memverifikasi konfigurasi yang aktif pada suatu topik
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics \
  --entity-name bank-transfers \
  --describe

Ringkasan #

  • Teorema CAP di Kafka: Diwujudkan dalam pilihan antara ketahanan data mutlak (Consistency) atau kelangsungan operasional tulis (Availability).
  • Status ISR: Hanya follower yang berada dalam kelompok ISR yang dinilai memiliki replika data yang valid dan aman untuk dipromosikan menjadi leader baru.
  • Konfigurasi unclean.leader.election.enable:
    • true: Memilih availability. Mengizinkan follower out-of-sync menjadi leader baru. Risiko kehilangan data dan pemotongan log sangat tinggi.
    • false (Default): Memilih consistency. Melarang keras promosi follower out-of-sync. Partisi menjadi offline sementara hingga broker ISR asli pulih.
  • Leader Epoch: Mekanisme modern yang menggunakan nomor generasi kepemimpinan untuk mencegah kerusakan log (split-brain) dan rekonsiliasi log yang cacat pasca-failover.
  • Rekomendasi Utama: Selalu setel parameter ke false untuk semua topik transaksional yang mengolah data stateful berharga tinggi (finansial, inventory, rekam medis).

← Sebelumnya: Acknowledgement   Berikutnya: Replication Factor →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact