In-Sync Replica #

Dalam sistem penyimpanan data terdistribusi yang mengandalkan redundansi, salah satu tantangan rekayasa terbesar adalah mendefinisikan kapan sebuah server cadangan (backup replica) dianggap “sehat” dan benar-benar sinkron dengan server utama (Leader). Di Apache Kafka, kelompok replika yang memenuhi kriteria keselarasan data yang ketat ini disebut In-Sync Replicas (ISR). ISR bertindak sebagai kuorum dinamis yang menjamin ketersediaan (availability) dan keandalan (durability) data kita. Melalui artikel ini, kita akan mengupas tuntas kriteria bagaimana sebuah replika follower dikategorikan masuk atau keluar dari kelompok ISR, bagaimana leader memanipulasi daftar ISR secara dinamis, hubungan erat antara parameter produser acks=all dengan parameter topik min.insync.replicas, serta cara memantau metrik krusial Under-Replicated Partitions (URP) guna menjaga stabilitas operasional kluster Kafka kita.


Apa Itu In-Sync Replicas (ISR)? #

Secara sederhana, In-Sync Replicas (ISR) adalah himpunan bagian (subset) dari seluruh replika partisi (termasuk Leader) yang saat ini secara aktif mengikuti penulisan data terbaru dari Leader. Jika kita memiliki topik dengan faktor replikasi 3, maka di kondisi ideal, ketiga replika tersebut (misalnya di Broker 1, Broker 2, dan Broker 3) semuanya berstatus sebagai anggota ISR.

Namun, status keanggotaan ISR ini bersifat dinamis. Sebuah Follower hanya diakui sebagai bagian dari ISR jika ia memenuhi dua kriteria utama berikut:

1. Kriteria Keaktifan Jaringan (Liveness) #

Follower harus terus-menerus terhubung ke kluster dan mengirimkan permintaan penarikan data (FetchRequest) ke Leader secara berkala. Jika Follower mengalami crash total, sistem operasi mati daya, atau proses JVM dibekukan oleh Garbage Collection (GC pause) yang terlalu lama, ia akan berhenti mengirim detak jantung (heartbeat) dan permintaan fetch ke Leader.

2. Kriteria Ketepatan Data (Lag Limit) #

Follower tidak boleh tertinggal terlalu jauh dari Leader dalam hal sinkronisasi data. Parameter tunggal paling penting yang mengontrol kriteria ini adalah replica.lag.time.max.ms (secara default bernilai 30000 milidetik atau 30 detik).

Kriteria ini diukur berdasarkan waktu: jika sebuah replika Follower gagal melakukan sinkronisasi hingga offset LEO lokalnya menyamai nilai LEO Leader dalam rentang waktu yang ditentukan (misalnya 30 detik), maka replika tersebut dicap lambat dan dikeluarkan dari kelompok ISR.

Di masa lalu (versi Kafka 0.9 ke bawah), terdapat parameter replica.lag.max.messages yang mengukur keterlambatan berdasarkan jumlah pesan (misal, jika tertinggal 4000 pesan, keluarkan dari ISR). Namun, parameter ini dibuang karena sering memicu ketidakstabilan kluster ketika terjadi lonjakan lalu lintas data secara tiba-tiba (traffic spikes); produsen mengirim pesan sangat cepat melampaui kemampuan replikasi sesaat, sehingga follower sehat pun dianggap tidak sinkron dan ditendang dari ISR secara massal, memicu kepanikan koordinasi pada kluster. Parameter berbasis waktu (replica.lag.time.max.ms) jauh lebih adil dan stabil dalam menilai kesehatan follower.


Siklus Hidup Replikasi: Shrinking dan Expanding ISR #

Proses perubahan keanggotaan ISR dikelola secara dinamis oleh broker yang bertindak sebagai Leader partisi dan dicatat di metadata kluster melalui interaksi asinkron:

1. Proses Penyusutan ISR (ISR Shrinking) #

Ketika sebuah Follower mengalami hambatan I/O disk yang berat atau isolasi jaringan parsial, kecepatan transfer datanya akan melambat.

  • Leader terus memantau posisi LEO dari setiap Follower melalui permintaan fetch yang masuk.
  • Jika Leader mendeteksi bahwa LEO sebuah Follower tidak pernah menyusul LEO Leader selama lebih dari batas waktu replica.lag.time.max.ms, Leader akan menandai Follower tersebut keluar dari ISR.
  • Leader mengirimkan permintaan pembaruan status ISR (Alter Partition Replicas) ke Controller.
  • Controller menulis daftar ISR baru ke metadata kluster (KRaft @metadata topic atau ZooKeeper), lalu menyebarkan metadata terbaru ini ke seluruh broker di dalam kluster agar produsen mengetahuinya.

2. Proses Perluasan ISR (ISR Expanding) #

Ketika gangguan pada Follower teratasi (misalnya, jaringan kembali stabil atau GC pause selesai), thread ReplicaFetcherThread pada Follower tersebut akan melanjutkan penarikan data secara maraton.

  • Follower menyalin semua data yang tertinggal dari log Leader.
  • Begitu LEO Follower berhasil menyusul LEO Leader, Leader menyadari bahwa data Follower sudah sinkron kembali.
  • Leader memasukkan kembali Follower tersebut ke dalam kelompok ISR.
  • Leader mengirimkan kembali permintaan pembaruan status ISR ke Controller untuk dicatat secara resmi ke metadata kluster.

Mari kita perhatikan visualisasi transisi status replika follower ini dalam diagram berikut:

flowchart TD
    subgraph ISRGroup ["Kelompok In-Sync Replicas (ISR)"]
        direction TB
        ActiveFollower["Active Follower <br/> (Sinkron / LEO Mengejar)"]
    end

    subgraph OutGroup ["Di Luar ISR (Out-of-Sync)"]
        direction TB
        LaggingFollower["Lagging Follower <br/> (Lag > replica.lag.time.max.ms)"]
    end

    ActiveFollower -->|"1. Terjadi Gangguan Jaringan / I/O Wait"| LaggingFollower
    LaggingFollower -->|"2. Proses Sinkronisasi Berhasil Menyusul LEO Leader"| ActiveFollower

    style ActiveFollower fill:#ddffdd,stroke:#88ff88
    style LaggingFollower fill:#ffdddd,stroke:#ff8888

Kolaborasi Keandalan: acks=all dan min.insync.replicas #

Pemahaman tentang ISR menjadi sangat krusial ketika kita membahas jaminan pengiriman data tanpa rugi (zero data loss). Parameter produser acks=all (atau acks=-1) dirancang untuk memastikan data berhasil ditulis ke beberapa server sebelum dianggap sukses. Namun, acks=all tidak bekerja sendirian; parameter ini sangat bergantung pada konfigurasi topik min.insync.replicas.

Jebakan Tanpa min.insync.replicas #

Bayangkan kita membuat topik dengan faktor replikasi 3 (Broker 1, Broker 2, Broker 3). Produsen mengirim data dengan acks=all. Nilai min.insync.replicas tidak kita setel (bernilai default 1).

  • Skenario Masalah: Broker 2 dan Broker 3 mati secara fisik. Daftar ISR menyusut secara otomatis hingga hanya menyisakan Broker 1 (Leader).
  • Perilaku Produsen: Saat produsen mengirim pesan dengan acks=all, broker Leader (Broker 1) akan memeriksa daftar ISR. Karena anggota ISR saat ini hanya 1 node (dirinya sendiri), Leader langsung menulis data ke log lokalnya dan mengirim respon sukses ke produsen. Produsen menganggap data aman karena telah direplikasi ke seluruh anggota ISR yang aktif.
  • Konsekuensi Kegagalan: Jika Broker 1 kemudian mengalami crash total sebelum Broker 2 dan Broker 3 hidup kembali, data yang baru saja dikirim produsen akan hilang selamanya. Kita telah kehilangan data meskipun menggunakan parameter teraman acks=all.

Solusi: Menyetel min.insync.replicas Secara Aman #

Untuk mencegah skenario buruk di atas, kita wajib menyetel konfigurasi tingkat topik atau tingkat broker global:

$$\text{min.insync.replicas} = 2$$

Ketika konfigurasi ini aktif disandingkan dengan acks=all:

  • Jika jumlah anggota aktif di dalam daftar ISR minimal 2 (misalnya Broker 1 dan Broker 2 hidup), aktivitas penulisan data dari produsen akan diterima dan diproses secara normal.
  • Jika jumlah anggota ISR turun di bawah 2 (misalnya hanya menyisakan Broker 1 karena yang lain mati), broker Leader akan menolak penulisan dari produsen dan melempar kesalahan NotEnoughReplicasException.
  • Produsen aplikasi kita akan menyadari kegagalan ini dan dapat melakukan penanganan darurat (seperti menahan data di memori atau mengalihkan penyimpanan sementara ke database lokal) daripada membiarkan data hilang tanpa sadar.

Perbandingan Durability vs Availability #

Menyetel kombinasi parameter ini adalah masalah kompromi (trade-off) antara keandalan data (durability) dengan ketersediaan sistem (availability):

Konfigurasi Topik Jaminan Keandalan (Durability) Ketersediaan Tulis (Availability) Konsekuensi Skenario Broker Mati
RF = 3, min.isr = 1, acks = all Rendah (Data bisa hilang jika 2 follower mati dan leader crash). Tinggi (Kluster tetap menerima penulisan selama minimal 1 broker ISR hidup). Jika 2 broker mati, sistem penulisan tetap berjalan lancar.
RF = 3, min.isr = 2, acks = all Tinggi (Data dijamin aman tersimpan di minimal 2 server). Moderat (Kluster menolak penulisan jika 2 broker mati secara bersamaan). Kita bisa menoleransi kematian 1 broker tanpa mengganggu fungsionalitas sistem.
RF = 3, min.isr = 3, acks = all Sangat Tinggi (Semua 3 server harus memiliki data). Sangat Rendah (Jika ada 1 saja broker yang sedang di-maintenance, tulis akan mogok total). Kematian 1 broker langsung mematikan aktivitas penulisan seluruh produsen.

Berdasarkan tabel di atas, konfigurasi RF = 3, min.insync.replicas = 2, acks = all adalah standar industri yang paling direkomendasikan untuk sistem produksi karena memberikan keseimbangan toleransi kegagalan terbaik (mampu menoleransi kematian 1 server tanpa menghentikan sistem).


Memantau Metrik Under-Replicated Partitions (URP) #

Dalam operasional harian, metrik pemantauan JMX paling kritis untuk mendeteksi masalah kesehatan kluster Kafka adalah UnderReplicatedPartitions (atau sering disingkat URP).

Apa Arti URP > 0? #

Metrik URP menghitung berapa banyak jumlah partisi di dalam broker di mana jumlah replika aktif saat ini lebih kecil daripada nilai replication.factor yang terkonfigurasi.

Misalnya, sebuah partisi memiliki faktor replikasi 3, tetapi daftar ISR saat ini hanya berisi 2 broker. Partisi ini akan dilaporkan sebagai Under-Replicated.

Mengapa URP Harus Diwaspadai? #

Memiliki partisi berstatus URP berarti kluster kita sedang berjalan dalam kondisi rentan. Jika terjadi satu kegagalan broker lagi pada partisi yang terdampak, kita akan mengalami kehilangan toleransi kegagalan penuh, memicu downtime penulisan jika min.insync.replicas disetel ke 2, atau bahkan kehilangan data permanen.

Kita wajib memasang sistem alarm (alerting system) pada alat pemantauan kita (seperti Prometheus/Grafana atau Datadog) jika metrik URP bernilai di atas 0 secara terus-menerus selama lebih dari 5 menit. URP sementara sering terjadi saat proses pemeliharaan rutin (rolling upgrades) ketika broker sengaja dimatikan satu per satu, namun URP yang menetap lama menandakan adanya masalah hardware disk atau kemacetan jaringan serius yang butuh intervensi manual tim infrastruktur kita.


Anti-pattern vs Solusi dalam Konfigurasi Keandalan #

Berikut adalah kesalahan umum dalam konfigurasi keandalan kluster beserta contoh kode penanganan kesalahan di sisi klien Java:

Kasus: Menolak Penulisan Akibat ISR Menyusut #

Ketika kluster kita mengalami kegagalan hardware hingga jumlah anggota ISR di bawah batas minimum, produsen akan menerima error NotEnoughReplicasException. Developer yang tidak mengantisipasi hal ini akan membiarkan aplikasi mereka crash secara fatal.

Mari kita bandingkan implementasi penanganan error yang salah dan yang benar:

// =========================================================================
// ANTI-PATTERN: Mengabaikan Penanganan NotEnoughReplicasException
// Aplikasi tidak siap menghadapi kegagalan kuorum dan membiarkan data hilang.
// =========================================================================
public void sendTransactionDataAntiPattern(KafkaProducer<String, String> producer, ProducerRecord<String, String> record) {
    // ✗ JANGAN: Mengirim tanpa callback asinkron atau tanpa recovery plan saat kluster menolak tulis.
    // Jika min.insync.replicas tidak terpenuhi, pengiriman ini akan memicu exception tak tertangani.
    producer.send(record); 
}

// =========================================================================
// SOLUSI YANG BENAR: Desain Tahan Banting dengan Fallback Storage
// Kita memantau kegagalan kuorum secara asinkron dan menyimpan data ke penyimpanan lokal
// sementara kluster Kafka dalam proses pemulihan (auto-recovery).
// =========================================================================
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReliableDataIngestor {
    private static final Logger log = LoggerFactory.getLogger(ReliableDataIngestor.class);

    public void sendDataWithFallback(KafkaProducer<String, String> producer, ProducerRecord<String, String> record) {
        producer.send(record, new Callback() {
            @Override
            public void onCompletion(RecordMetadata metadata, Exception exception) {
                if (exception != null) {
                    if (exception instanceof org.apache.kafka.common.errors.NotEnoughReplicasException || 
                        exception instanceof org.apache.kafka.common.errors.NotEnoughReplicasAfterAppendException) {
                        
                        // ✓ BENAR: Kuorum ISR tidak terpenuhi. Jangan buang datanya!
                        // Alihkan data ke penyimpanan darurat lokal (e.g. disk lokal, SQLite, atau queue lokal)
                        log.error("Gagal mengirim data karena kuorum ISR menyusut di bawah batas min.insync.replicas: " 
                            + exception.getMessage());
                        saveToLocalQueue(record);
                        
                    } else {
                        log.error("Pengiriman gagal akibat error non-kuorum: " + exception.getMessage());
                        saveToLocalQueue(record);
                    }
                } else {
                    log.info("Data sukses berkomit di ISR. Partisi: {}, Offset: {}", 
                        metadata.partition(), metadata.offset());
                }
            }
        });
    }

    private void saveToLocalQueue(ProducerRecord<String, String> record) {
        // Implementasi logika penyimpanan darurat lokal untuk pengiriman ulang (retry) nanti
        System.out.printf("✓ Menyimpan pesan dengan key '%s' ke penyimpanan lokal darurat.\n", record.key());
    }
}

Ringkasan #

  • In-Sync Replicas (ISR) — Himpunan replika partisi (termasuk Leader) yang secara aktif menyalin data terbaru dari Leader dan dianggap sinkron oleh sistem.
  • Kriteria Lag Waktu — Follower dianggap keluar dari ISR jika ia gagal melakukan sinkronisasi data hingga menyamai LEO Leader dalam batas waktu parameter replica.lag.time.max.ms (default 30 detik).
  • Proses Dinamis Alter ISR — Leader partisi bertugas mendeteksi lag follower secara langsung dan mengirim perintah Alter Partition Replicas ke Controller untuk mengubah status ISR di metadata.
  • min.insync.replicas — Parameter pengontrol jumlah minimal replika aktif di dalam ISR yang harus mengonfirmasi penulisan data sebelum broker mengembalikan sinyal sukses ke produsen.
  • acks=all — Parameter produser yang menuntut konfirmasi penulisan dari seluruh anggota ISR aktif, wajib dipasangkan dengan min.insync.replicas=2 untuk jaminan zero data loss.
  • Under-Replicated Partitions (URP) — Metrik pemantauan JMX krusial yang mendeteksi adanya partisi kluster yang kehilangan redundansi akibat broker mati atau gangguan I/O disk.
  • Toleransi Kerusakan — Konfigurasi standar industri teraman untuk produksi adalah menyetel Replication Factor = 3, min.insync.replicas = 2, dan acks = all.

← Sebelumnya: Leader & Follower   Berikutnya: Zookeeper vs KRaft →

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