Acks, Retries, & Linger.ms #
Ketika kita membangun aplikasi klien produser untuk mengirimkan pesan ke Apache Kafka, performa bawaan (default configuration) yang disediakan oleh pustaka klien tidak selalu sesuai dengan kebutuhan sistem kita. Kafka dirancang untuk melayani berbagai jenis kebutuhan sistem yang sangat bertolak belakang: dari sistem pencatatan sensor IoT yang membutuhkan throughput jutaan pesan per detik tanpa memedulikan kehilangan sebagian kecil data, hingga sistem transaksi keuangan perbankan yang menuntut keandalan mutlak tanpa boleh kehilangan satu pun pesan, meskipun harus mengorbankan sedikit kecepatan. Dua pilar utama yang menentukan perilaku ini berada di tangan penyetelan parameter produser kita. Memahami cara melakukan tuning pada parameter ketahanan data seperti acks dan retries, serta parameter optimasi throughput seperti linger.ms dan batch.size adalah keterampilan wajib untuk merancang arsitektur data yang tangguh dan efisien.
Membedah Konfigurasi Ketahanan Data: acks #
Parameter acks (acknowledgement) mengontrol jumlah broker dalam kluster yang harus menerima pesan dan mengirimkan konfirmasi kembali ke produser sebelum pengiriman pesan tersebut dianggap sukses. Penyetelan parameter ini merupakan kompromi (trade-off) langsung antara tingkat ketahanan data (durability) dengan kecepatan pengiriman (latency).
Terdapat tiga nilai konfigurasi yang dapat kita berikan pada parameter acks:
1. acks=0 (Tanpa Konfirmasi)
#
Produser dianggap telah sukses mengirimkan pesan segera setelah ia menuliskannya ke socket jaringan TCP. Produser tidak menunggu konfirmasi apa pun dari broker Kafka.
- Kelebihan: Latensi transmisi sangat rendah dan throughput maksimal karena produser tidak terhambat oleh proses I/O broker.
- Kekurangan: Risiko kehilangan data sangat tinggi. Jika broker tujuan mati, atau disk penuh tepat setelah pesan masuk ke socket, produser tidak akan pernah tahu bahwa pesan tersebut telah hilang dan akan tetap melanjutkan pengiriman berikutnya.
- Use Case: Pengumpulan log server yang tidak kritis, pelacakan metrik real-time.
2. acks=1 (Hanya Konfirmasi Leader)
#
Produser akan menunggu konfirmasi tertulis sukses hanya dari broker yang bertindak sebagai Leader partisi tujuan. Leader akan menulis data ke log komit lokalnya (menjamin penyimpanan ke disk) sebelum mengirimkan ACK sukses kembali ke produser.
- Kelebihan: Memberikan keseimbangan moderat antara throughput yang cepat dengan jaminan ketahanan data yang wajar.
- Kekurangan: Masih terdapat celah kehilangan data. Jika leader mati sesaat setelah mengirimkan ACK sukses ke produser, namun sebelum broker pengikut (Followers) sempat menduplikasi data tersebut via replikasi, data tersebut akan hilang secara permanen ketika salah satu follower yang tertinggal dipromosikan menjadi leader baru.
- Use Case: Sistem tracking perilaku klik pengguna (clickstream), pelaporan statistik non-finansial.
3. acks=all atau acks=-1 (Konfirmasi Seluruh ISR)
#
Produser baru akan menganggap pengiriman pesan sukses setelah leader partisi menerima konfirmasi dari seluruh replika yang tergabung dalam In-Sync Replicas (ISR).
- Kelebihan: Keandalan data mutlak. Selama minimal satu broker ISR cadangan tetap hidup, pesan dijamin tidak akan hilang dari kluster.
- Kekurangan: Latensi pengiriman tertinggi karena produser terikat oleh waktu sinkronisasi replikasi jaringan antar broker.
- Use Case: Transaksi pembayaran finansial, pembuatan faktur invoice, catatan audit keamanan.
[!WARNING] Properti
acks=alltidak memberikan jaminan keamanan data penuh jika parametermin.insync.replicasdi sisi broker topik disetel ke nilai1. Jikamin.insync.replicas=1, maka meskipun produser memintaacks=all, broker leader akan langsung mengirimkan ACK sukses meskipun tidak ada follower aktif lainnya yang mereplikasi data tersebut (karena leader menilai dirinya sendiri sudah cukup memenuhi syarat minimum 1 ISR). Untuk keandalan finansial, selalu setelmin.insync.replicas=2pada broker dengan Replication Factor bernilai 3.
Mengatasi Gangguan Jaringan: retries dan Timeout Parameters #
Koneksi jaringan antar aplikasi produser dengan broker Kafka tidak selalu stabil. Adanya gangguan jaringan sesaat (network glitches), proses pemilihan leader baru (leader election), atau proses restart broker secara bergantian (rolling restart) dapat memicu kegagalan pengiriman pesan. Di sinilah peran parameter ketahanan error aktif bekerja.
1. Properti retries
#
Parameter ini menentukan berapa kali produser akan mencoba mengirimkan kembali (retry) pesan yang gagal akibat masalah sementara (retriable errors seperti masalah timeout jaringan atau broker leader sedang berpindah).
- Konfigurasi Modern: Pada klien produser Kafka versi terbaru, default dari
retriestelah disetel ke nilai maksimum integer (Integer.MAX_VALUE). Produser akan terus mencoba mengirim ulang pesan hingga batas durasi waktu keseluruhan habis.
2. Properti retry.backoff.ms
#
Secara default, jika sebuah pengiriman gagal, produser akan segera melakukan retry. Namun, membombardir broker yang sedang sibuk atau mati dengan permintaan terus-menerus tanpa jeda dapat memperparah kondisi broker tersebut.
- Cara Kerja: Parameter
retry.backoff.ms(default: 100 ms) memberikan jeda istirahat (waktu tunggu) bagi produser sebelum melakukan upaya retry berikutnya, memberikan waktu bagi kluster broker untuk pulih.
3. Properti delivery.timeout.ms
#
Ini adalah batas waktu total terpenting di sisi produser. Properti delivery.timeout.ms (default: 120.000 ms atau 2 menit) membatasi durasi total dari pemanggilan .send() hingga produser menerima ACK sukses atau menyerah dan melempar exception error.
- Rumus Batas Waktu: Nilai
delivery.timeout.msharus selalu lebih besar atau sama dengan jumlah dari parameterrequest.timeout.msdanlinger.ms:
$$\text{delivery.timeout.ms} \ge \text{request.timeout.ms} + \text{linger.ms}$$
Jika pesan tidak kunjung menerima ACK sukses dalam rentang waktu default 2 menit (karena jaringan mati total), pesan tersebut akan dibuang dari antrean Record Accumulator dan aplikasi kita akan menerima kegagalan pengiriman.
Tuning Throughput vs Latensi: linger.ms dan batch.size #
Siklus pengiriman pesan secara asinkron di produser Kafka sangat bertumpu pada efisiensi pengelompokan pesan di dalam memori buffer Record Accumulator. Kita dapat menaikkan performa throughput aplikasi kita dengan mengatur dua konfigurasi berikut:
1. Properti batch.size
#
Menentukan kapasitas ukuran memori maksimum (dalam satuan byte) yang dialokasikan untuk menampung pesan-pesan per partisi di dalam Record Accumulator. Defaultnya adalah 16.384 byte (16 KB).
- Optimasi: Jika pesan-pesan kita berukuran besar atau throughput aplikasi kita sangat padat, menaikkan
batch.sizemenjadi 64 KB atau 128 KB sangat disarankan agar buffer tidak terlalu cepat penuh dan memicu pengiriman paket jaringan kecil secara berulang.
2. Properti linger.ms
#
Menentukan berapa lama (dalam milidetik) produser akan menunda pengiriman batch di memori sebelum diambil oleh Sender Thread. Secara default, properti ini disetel ke 0 ms (produser akan mengirimkan pesan sesegera mungkin tanpa menunggu pesan lain masuk ke batch yang sama).
- Optimasi: Dengan memberikan sedikit waktu tunggu, misalnya
linger.ms=20(20 milidetik), kita memberi kesempatan bagi utas aplikasi utama untuk menumpuk lebih banyak pesan ke dalam batch yang sama sebelum Sender Thread mengirimkannya ke socket. Hal ini melipatgandakan throughput dengan menambah sedikit overhead latensi 20 milidetik.
Diagram Mermaid: Hubungan Linger.ms terhadap Transmisi Batch #
Berikut adalah visualisasi bagaimana parameter linger.ms dan batch.size berkolaborasi di dalam Record Accumulator untuk mengontrol keputusan Sender Thread sebelum mengirim data ke broker:
flowchart TD
subgraph Accumulator ["Record Accumulator (Penyangga Memori)"]
direction TB
Rec1["Pesan 1 (T=0ms)"] --> B1["Batch Partisi 0"]
Rec2["Pesan 2 (T=2ms)"] --> B1
Rec3["Pesan 3 (T=5ms)"] --> B1
end
subgraph Decision ["Sender Thread Decision Loop"]
direction TB
Cond1{"Ukuran Batch >= batch.size?"}
Cond2{"Waktu Tertahan >= linger.ms?"}
end
B1 --> Cond1
Cond1 -- "Ya (Batch Penuh, e.g., 16KB)" --> SendNow["Kirim Segera ke Jaringan"]
Cond1 -- "Tidak" --> Cond2
Cond2 -- "Ya (e.g., linger.ms=20ms habis)" --> SendNow
Cond2 -- "Tidak" --> Wait["Tahan di Buffer (Akumulasi Pesan Baru)"]
SendNow --> Broker["Broker Kafka"]
style Accumulator stroke:#e5e7eb
style Decision stroke:#e5e7eb
style B1 stroke:#0288d1,stroke-width:2px
style SendNow stroke:#2e7d32,stroke-width:2px
style Wait stroke:#f57c00,stroke-width:2px
Interaksi Max In-Flight Requests dengan Urutan Pesan (Ordering) #
Salah satu parameter terpenting yang sering kali terabaikan dalam merancang stabilitas produser adalah max.in.flight.requests.per.connection (default: 5). Parameter ini menentukan berapa banyak permintaan pengiriman (produce requests) tak bertuan (belum memperoleh ACK sukses dari broker) yang boleh dikirimkan produser melalui satu koneksi socket TCP secara bersamaan.
Mengapa Properti ini Memengaruhi Urutan Data? #
Jika properti ini disetel ke nilai yang lebih besar dari 1 (misalnya menggunakan nilai default 5) dan kita membiarkan enable.idempotence=false, kita berisiko mengalami masalah pengurutan pesan yang terbalik (out-of-order messages) apabila terjadi kegagalan jaringan sementara.
Mari kita simulasikan skenario kegagalan pengiriman berikut:
- Produser mengirim Batch A ke broker. Karena belum ada ACK, Batch A berstatus in-flight.
- Tanpa menunggu ACK Batch A, produser mengirim Batch B secara asinkron. Sekarang ada 2 request in-flight.
- Batch A gagal diterima broker karena terjadinya gangguan jaringan sementara (misalnya broker leader sedang sibuk melakukan pemilihan leader baru).
- Batch B sukses diterima broker dan berhasil ditulis ke log komit partisi.
- Produser menerima sinyal kegagalan untuk Batch A. Karena parameter
retriesaktif, produser mencoba mengirim ulang Batch A. - Upaya pengiriman ulang Batch A sukses ditulis ke broker.
- Hasil Akhir yang Merusak: Di dalam log komit broker, Batch B tertulis lebih dahulu daripada Batch A. Urutan pesan terbalik secara permanen!
Solusi Mengatasi Masalah Urutan Pesan #
- Cara Lama (Sebelum Idempotency): Kita terpaksa menyetel
max.in.flight.requests.per.connection=1. Konfigurasi ini memaksa produser bertindak secara sinkronis — menanti ACK Batch A sebelum Batch B boleh dikirim. Namun, ini membatasi throughput pengiriman karena bandwidth jaringan tidak pernah termanfaatkan secara penuh. - Cara Modern (Rekomendasi): Kita mempertahankan nilai
max.in.flight.requests.per.connectionantara1hingga5, dan wajib menyetelenable.idempotence=true. Idempotensi menggunakan nomor urut (sequence number) untuk melacak urutan batch di sisi broker. Broker akan menolak menulis Batch B jika mendeteksi Batch A yang berurutan sebelumnya belum masuk, sehingga memaksa broker menyusun kembali batch yang berantakan secara benar sebelum ditulis ke disk.
Kompresi Data pada Produser: Gzip, Snappy, Lz4, vs Zstd #
Secara default, pesan dikirimkan dari produser ke broker dalam bentuk data mentah tidak terkompresi. Jika aplikasi kita memproses jutaan pesan berukuran sedang hingga besar setiap hari, mengaktifkan kompresi data via properti compression.type di sisi produser adalah langkah optimasi yang sangat signifikan.
Keuntungan Kompresi End-to-End Kafka #
Siklus kompresi di Kafka sangat efisien karena beroperasi secara end-to-end:
- Produser melakukan kompresi pada batch data.
- Data dikirimkan dalam bentuk biner terkompresi melalui jaringan, menghemat bandwidth.
- Broker menyimpan byte terkompresi secara langsung ke disk tanpa melakukan dekompresi (menghemat memori dan CPU broker).
- Konsumen menerima byte terkompresi dan melakukan dekompresi di sisi aplikasi mereka.
Perbandingan Algoritma Kompresi #
| Tipe Kompresi | Rasio Kompresi | Kecepatan Kompresi | Beban CPU Klien | Karakteristik Utama |
|---|---|---|---|---|
gzip |
Sangat Tinggi | Lambat | Tinggi | Rasio terbaik, cocok untuk kompresi arsip data log dingin (cold data). |
snappy |
Sedang | Sangat Cepat | Sangat Rendah | Dikembangkan oleh Google, didesain untuk kecepatan I/O tinggi dengan beban CPU minimal. |
lz4 |
Sedang | Sangat Cepat | Sangat Rendah | Mirip dengan Snappy, sangat optimal untuk lalu lintas data real-time cepat. |
zstd |
Tinggi | Cepat | Sedang | Dikembangkan oleh Facebook, memberikan rasio mendekati Gzip dengan kecepatan mendekati Snappy. |
Bagi sebagian besar aplikasi produksi umum, memilih snappy atau lz4 adalah keputusan teraman untuk meminimalkan beban CPU pada aplikasi produser kita, sementara zstd adalah pilihan ideal jika bandwidth jaringan kita sangat terbatas namun kita masih memiliki daya komputasi CPU cadangan yang memadai.
Implementasi Konfigurasi Java: High-Throughput vs High-Durability #
Sebagai arsitek sistem, kita harus mampu mengonfigurasi produser agar sesuai dengan karakteristik data bisnis kita. Berikut adalah kode perbandingan antara konfigurasi produser yang salah (anti-pattern) dengan konfigurasi optimal untuk dua use case yang berbeda:
// ANTI-PATTERN: Konfigurasi default atau campuran tidak terarah
// Prosedur transaksi keuangan yang tidak aman karena acks=1 dan retries dimatikan
public class NaiveFinancialProducer {
public Properties getProperties() {
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
// ✗ Sangat berbahaya untuk transaksi keuangan: data bisa hilang jika leader restart
props.put("acks", "1");
// ✗ Mematikan retry otomatis memaksa aplikasi menangani error transient secara manual
props.put("retries", "0");
// ✗ linger.ms terlalu tinggi tanpa alasan jelas di sistem transaksional sensitif latensi
props.put("linger.ms", "5000");
return props;
}
}
// BENAR: Konfigurasi Skenario 1 - High Durability (Financial Grade, No Data Loss)
public class HighDurabilityProducer {
public Properties getProperties() {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
// ✓ Keandalan Mutlak: Menunggu konfirmasi dari seluruh ISR
props.put(ProducerConfig.ACKS_CONFIG, "all");
// ✓ Lakukan retry otomatis hingga batas waktu habis demi menghindari kehilangan data transient
props.put(ProducerConfig.RETRIES_CONFIG, Integer.MAX_VALUE);
// ✓ Aktifkan idempotensi produser untuk mencegah duplikasi akibat retry jaringan
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");
// ✓ Batasi in-flight request ke 5 untuk menjamin urutan data dan kompatibilitas idempotensi
props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, "5");
// ✓ Batas waktu keseluruhan penyerahan record sebelum error dilempar ke aplikasi
props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, "120000"); // 2 menit
return props;
}
}
// BENAR: Konfigurasi Skenario 2 - High Throughput (Massive Logging/IoT Clickstream)
public class HighThroughputProducer {
public Properties getProperties() {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
// ✓ Cukup tunggu konfirmasi leader untuk menghemat latensi transmisi RTT
props.put(ProducerConfig.ACKS_CONFIG, "1");
// ✓ Berikan jeda 20ms agar utas aplikasi dapat menumpuk pesan ke satu batch
props.put(ProducerConfig.LINGER_MS_CONFIG, "20");
// ✓ Naikkan kapasitas batch memori menjadi 64KB (dari default 16KB)
props.put(ProducerConfig.BATCH_SIZE_CONFIG, String.valueOf(64 * 1024));
// ✓ Aktifkan kompresi data biner (misal: Snappy atau Zstd) untuk menghemat bandwidth
props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy");
// ✓ Alokasikan memori penyangga buffer pool yang lebih luas untuk menghindari backpressure
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, String.valueOf(64 * 1024 * 1024)); // 64 MB
return props;
}
}
Rekomendasi Penyetelan untuk Berbagai Use Case #
Untuk mempermudah pengambilan keputusan saat merancang sistem di lingkungan produksi, berikut adalah matriks acuan parameter konfigurasi berdasarkan use case:
| Use Case | acks | linger.ms | compression.type | enable.idempotence | min.insync.replicas (broker) |
|---|---|---|---|---|---|
| E-Commerce Payment | all |
0–5 |
lz4 |
true |
2 |
| IoT Sensor Metrics | 1 |
50–100 |
snappy |
false |
1 |
| Log Aggregation | 1 |
20–50 |
zstd |
false |
1 |
| Audit Security Logs | all |
10 |
zstd |
true |
2 |
Ringkasan #
- ACKS Levels: Penyetelan properti
acksmengontrol tingkat konfirmasi broker (0,1,all) yang menentukan keseimbangan (trade-off) latensi vs keandalan data.- min.insync.replicas: Nilai
acks=alldi produser membutuhkan konfigurasimin.insync.replicas >= 2di sisi broker untuk menjamin replikasi data ke pengikut (followers).- Linger & Batch: Menggabungkan parameter
linger.ms(misal 20ms) denganbatch.size(misal 64KB) dapat memangkas latensi jaringan dan melipatgandakan throughput.- Idempotence: Selalu aktifkan
enable.idempotence=truesaat menggunakan retry tinggi untuk membuang salinan data duplikat secara aman di sisi broker.- Delivery Timeout: Parameter
delivery.timeout.msmembatasi durasi toleransi produser melakukan upaya coba ulang sebelum melempar exception ke utas aplikasi utama.
← Sebelumnya: Key vs No-Key Message Berikutnya: Idempotent Producer →