Partition & Broker Sizing: Merancang Jumlah Partisi dan Skala Kluster Apache Kafka #

Dalam merancang arsitektur topik dan kluster di Apache Kafka, dua pertanyaan paling mendasar yang harus kita jawab adalah: “Berapa banyak partisi yang kita butuhkan untuk topik ini?” dan “Berapa banyak server broker yang harus kita sediakan di dalam kluster?” Sayangnya, keputusan ini sering kali diambil tanpa perhitungan yang matang. Sebagian tim memilih membuat ribuan partisi untuk semua topik dengan asumsi “semakin banyak semakin baik,” sementara sebagian lainnya membiarkan jumlah partisi default bernilai satu, sehingga aplikasi mereka tidak dapat berskala.

Kesalahan dalam menentukan ukuran partisi dan jumlah broker dapat berakibat fatal di kemudian hari. Kelebihan partisi (Partition Bloat) akan membebani koordinasi metadata, memperlambat proses pemulihan saat broker crash (recovery latency), meningkatkan konsumsi memori JVM, dan menghabiskan batas file descriptor sistem operasi. Sebaliknya, kekurangan partisi akan membatasi kapasitas paralelisme konsumen, memicu antrean data (consumer lag), dan membatasi pemanfaatan sumber daya server yang telah kita sewa.

Dalam panduan ini, kita akan membedah korelasi antara partisi dengan paralelisme, mempelajari formula matematika untuk menghitung jumlah partisi ideal berdasarkan target throughput, memahami risiko partition bloat di era ZooKeeper vs KRaft, serta menghitung kebutuhan skala fisik node broker untuk kluster produksi kita.

Hubungan Antara Partisi, Paralelisme, dan Skalabilitas #

Untuk memahami pentingnya penentuan ukuran partisi, kita harus mengingat kembali prinsip dasar arsitektur Kafka: Partisi adalah unit terkecil skalabilitas dan paralelisme di Kafka.

flowchart TD
    subgraph Partitions ["Topik payment.orders dengan 3 Partisi"]
        direction TB
        P0["Partisi 0"]
        P1["Partisi 1"]
        P2["Partisi 2"]
    end
    subgraph Consumers ["Consumer Group"]
        direction TB
        CA["Konsumen A (Aktif memproses data)"]
        CB["Konsumen B (Aktif memproses data)"]
        CC["Konsumen C (Aktif memproses data)"]
        CD["Konsumen D (Idle / Menganggur - Tidak kebagian partisi)"]
    end
    P0 --> CA
    P1 --> CB
    P2 --> CC

Di sisi konsumen, satu partisi hanya dapat dikonsumsi oleh maksimal satu anggota konsumen di dalam satu Consumer Group yang sama pada satu waktu. Jika kita memiliki topik dengan 3 partisi, lalu kita menjalankan 4 instans aplikasi konsumen, instans ke-4 akan menganggur (idle) tanpa menerima data. Dengan demikian, jumlah partisi menentukan batas atas dari kemampuan paralelisme aplikasi konsumen kita.


Formula Menghitung Jumlah Partisi Ideal #

Untuk menghindari tebak-tebakan, kita dapat menentukan jumlah partisi per topik secara ilmiah menggunakan formula throughput target.

Formula matematika untuk menghitung jumlah partisi minimum adalah:

$$\text{Jumlah Partisi} = \max\left(\frac{T}{p}, \frac{T}{c}\right)$$

Di mana:

  • $T$ (Target Throughput): Total throughput tulis/baca yang ingin dicapai oleh bisnis pada topik tersebut (dalam satuan MB/s atau jumlah pesan per detik).
  • $p$ (Throughput Produser Tunggal): Throughput maksimum yang dapat dikirim oleh satu thread produser tunggal ke satu partisi tanpa hambatan (biasanya bernilai $\approx 10 \text{ hingga } 20 \text{ MB/s}$ tergantung pada skema enkripsi dan kompresi).
  • $c$ (Throughput Konsumen Tunggal): Throughput maksimum yang dapat diproses oleh satu instans konsumen tunggal dari satu partisi. Konsumen biasanya memproses data lebih lambat karena harus melakukan operasi I/O (seperti menulis ke database relasional, memanggil API eksternal, atau melakukan pemrosesan memori yang kompleks). Throughput konsumen berkisar antara $\approx 1 \text{ hingga } 5 \text{ MB/s}$.

Contoh Kasus Kalkulasi Partisi #

Misalkan kita ingin membangun topik untuk sistem pemrosesan log pembayaran:

  • Target Throughput ($T$): Bisnis membutuhkan sistem untuk mampu memproses aliran data puncak sebesar $80 \text{ MB/s}$.
  • Performa Produser ($p$): Berdasarkan uji coba, satu produser Java kita dapat mengirim data rata-rata sebesar $20 \text{ MB/s}$.
  • Performa Konsumen ($c$): Aplikasi konsumen kita harus menulis data ke database PostgreSQL, yang membatasi kecepatan pemrosesan konsumen hanya sebesar $4 \text{ MB/s}$ per thread.

Mari kita masukkan angka-angka tersebut ke dalam formula: $$\text{Partisi dari sisi Produser} = \frac{80 \text{ MB/s}}{20 \text{ MB/s}} = 4 \text{ partisi}$$ $$\text{Partisi dari sisi Konsumen} = \frac{80 \text{ MB/s}}{4 \text{ MB/s}} = 20 \text{ partisi}$$ $$\text{Jumlah Partisi Minimum} = \max(4, 20) = 20 \text{ partisi}$$

Dalam skenario ini, kita wajib membuat minimal 20 partisi untuk topik tersebut. Jumlah ini menjamin bahwa kita dapat menjalankan hingga 20 instans konsumen secara paralel untuk mengimbangi laju pengiriman data produser sebesar $80 \text{ MB/s}$ tanpa memicu terjadinya penumpukan lag.


Diagram Alur Keputusan Penentuan Partisi (Decision Tree) #

Berikut adalah diagram alur keputusan (decision tree) untuk memandu kita dalam memilih jumlah partisi yang tepat berdasarkan batasan fungsional aplikasi:

flowchart TD
    Start["Mulai Perancangan Topik"] --> Q1{"Apakah urutan pesan mutlak diperlukan?"}
    
    Q1 -- "Ya, Seluruh Topik" --> SinglePart["Gunakan 1 Partisi<br/>(Paralelisme terbatas pada 1 konsumen)"]
    Q1 -- "Ya, Berdasarkan Kunci (Key)" --> Q2["Identifikasi Kunci Pesan (e.g., user_id)"]
    Q1 -- "Tidak (Urutan Bebas)" --> CalcMath["Hitung Partisi dengan Formula:<br/>Max(T/p, T/c)"]
    
    Q2 --> CalcMath
    
    CalcMath --> DoubleCheck{"Apakah hasil hitung > 100?"}
    
    DoubleCheck -- "Ya" --> Q3{"Apakah menggunakan mode KRaft?"}
    DoubleCheck -- "Tidak" --> RoundUp["Bulatkan ke atas untuk pertumbuhan masa depan<br/>(e.g., kelipatan jumlah broker)"]
    
    Q3 -- "Ya" --> ApplyKraft["Gunakan angka tersebut secara aman"]
    Q3 -- "Tidak (ZooKeeper)" --> LimitZK["Batasi partisi, lakukan optimasi kode konsumen<br/>agar throughput per thread naik"]
    
    RoundUp --> End["Terapkan Jumlah Partisi"]
    ApplyKraft --> End
    LimitZK --> End

Uji Kapasitas: Mengukur Kecepatan Produser & Konsumen Riil #

Untuk mendapatkan nilai $p$ (throughput produser) dan $c$ (throughput konsumen) secara akurat di lingkungan jaringan kita sendiri, kita disarankan menggunakan perkakas uji beban (load testing) bawaan Apache Kafka.

1. Mengukur Performa Produser dengan kafka-producer-perf-test.sh #

Perintah ini mengirimkan aliran pesan buatan (synthetic messages) ke partisi tunggal untuk mengukur kapasitas maksimal penulisan:

kafka-producer-perf-test.sh --topic test-perf-topic \
  --num-records 1000000 \
  --record-size 1024 \
  --throughput -1 \
  --producer-props bootstrap.servers=localhost:9092 \
    acks=1 \
    compression.type=zstd

Output Contoh:

1000000 records sent, 98212.1 records/sec (95.91 MB/sec), 15.2 ms avg latency, 450.0 ms max latency.

Dari contoh di atas, produser kita mampu mengirim data hingga $95 \text{ MB/s}$ per partisi menggunakan kompresi Zstd.

2. Mengukur Performa Konsumen dengan kafka-consumer-perf-test.sh #

Perintah ini mengunduh data dari topik target secepat mungkin untuk mengukur kecepatan baca optimal:

kafka-consumer-perf-test.sh --bootstrap-server localhost:9092 \
  --topic test-perf-topic \
  --messages 1000000 \
  --threads 1

Output Contoh:

start.time, end.time, data.consumed.in.MB, throughput.in.MB_sec
2026-06-08 16:10:00, 2026-06-08 16:10:15, 1024.0000, 68.2667

Dari contoh ini, satu thread konsumen Java mampu memproses data sebesar $68 \text{ MB/s}$ (tanpa beban pemrosesan database). Jika kode aplikasi kita ditambahkan I/O database, angka ini akan turun drastis, misalnya menjadi $5 \text{ MB/s}$, dan angka inilah yang harus kita masukkan sebagai variabel $c$ dalam formula sizing.


Menambah Partisi secara Dinamis: Implikasi dan Bahaya #

Salah satu fitur menarik di Kafka adalah kita dapat menambah jumlah partisi pada topik yang sudah berjalan secara dinamis menggunakan perintah kafka-topics.sh --alter. Namun, kita harus sangat berhati-hati karena tindakan ini memiliki konsekuensi serius terhadap integritas logika data:

  1. Kerusakan Rute Kunci (Key-Routing Breaks): Secara default, Kafka mengarahkan pesan yang memiliki kunci menggunakan algoritma hashing: $$\text{Partisi} = \text{hash}(\text{key}) \pmod{\text{Jumlah Partisi}}$$ Jika jumlah partisi berubah di tengah jalan (misalnya dari 10 menjadi 15), rumus pembagian sisa (modulo) di atas akan menghasilkan partisi tujuan yang berbeda untuk kunci yang sama. Akibatnya, pesan-pesan baru dengan kunci user-123 akan terkirim ke partisi yang berbeda dari pesan-pesan lama, sehingga garansi urutan pesan per kunci menjadi rusak.
  2. Tidak Bisa Dikurangi (No Partition Scale-Down): Kafka tidak mendukung pengurangan jumlah partisi (skala turun). Sekali topik dikonfigurasi memiliki 50 partisi, kita tidak dapat menurunkannya menjadi 20. Satu-satunya jalan adalah menghapus topik tersebut (kehilangan data) atau membuat topik baru dengan nama berbeda lalu melakukan migrasi data.

Merancang Ukuran Internal Topic: __consumer_offsets #

Setiap kali konsumen melakukan komitmen offset, Kafka menulis pesan komitmen tersebut ke topik internal bernama __consumer_offsets. Karena topik ini melayani seluruh grup konsumen di dalam kluster, perencanaannya harus dilakukan secara cermat di tingkat broker (server.properties):

  • Jumlah Partisi Default: Ditentukan oleh parameter offsets.topic.num.partitions (default $50$). Angka ini sangat ideal untuk sebagian besar kluster kelas menengah. Jangan disetel ke nilai default minimal (seperti 1) di produksi agar beban komitmen dapat tersebar merata ke beberapa broker.
  • Faktor Replikasi: Ditentukan oleh offsets.topic.replication.factor (default $3$). Di produksi, pastikan parameter ini disetel ke 3 untuk menjamin bahwa komitmen offset konsumen tetap berjalan meskipun salah satu broker leader koordinasi mengalami kegagalan.

Dampak Negatif Partition Bloat (Kelebihan Partisi) #

Jika partisi adalah kunci skalabilitas, mengapa kita tidak membuat 1.000 partisi untuk setiap topik sejak awal? Fenomena kelebihan partisi ini dikenal sebagai Partition Bloat dan membawa dampak buruk yang nyata bagi stabilitas operasional broker.

1. Keterlambatan Pemulihan Metadata (Failover Latency) #

Setiap partisi pada broker dipimpin oleh satu broker leader dan direplikasi ke beberapa broker follower. Jika sebuah broker yang menampung 10.000 partisi mengalami crash, controller kluster harus memproses perubahan kepemimpinan (leader election) sebanyak 10.000 kali.

  • Proses pemilihan ini memerlukan penulisan perubahan status metadata ke KRaft log atau koordinasi dengan ZooKeeper.
  • Semakin banyak partisi yang terlibat, semakin lama waktu yang dibutuhkan kluster untuk pulih kembali. Selama masa pemulihan ini, beberapa partisi akan berstatus offline dan tidak dapat diakses oleh klien.

2. Kebocoran Berkas Descriptor Sistem Operasi (Open Files Exhaustion) #

Pada tingkat sistem operasi (Linux), setiap partisi data diwakili oleh sebuah direktori fisik di dalam disk. Di dalam direktori tersebut, setiap log segment memiliki setidaknya tiga file aktif: file data .log, file indeks .index, dan file waktu .timeindex.

  • Jika sebuah broker mengelola 5.000 partisi, server tersebut minimal membuka $5.000 \times 3 = 15.000$ berkas secara aktif.
  • Jika batas file descriptor OS (ulimit -n) disetel terlalu rendah (misalnya default Linux hanya 1024), broker akan langsung mengalami crash dengan pesan kesalahan Too many open files.

3. Konsumsi Memori JVM Heap Berlebih #

Setiap partisi memerlukan alokasi memori buffer di dalam JVM heap broker untuk mengelola thread replikasi, melacak posisi offset, dan mengelola cache metadata. Menimbun terlalu banyak partisi pada satu broker JVM akan memicu aktivitas Garbage Collection (GC) yang sangat sering, memperbesar durasi jeda GC (stop-the-world), dan memicu resiko Out of Memory (OOM).


Batas Partisi pada ZooKeeper vs KRaft #

Batas aman jumlah partisi sangat bergantung pada mode koordinasi metadata kluster yang kita gunakan:

1. Kluster Berbasis ZooKeeper (Legacy) #

Pada mode ZooKeeper, seluruh perubahan status kepemimpinan partisi harus ditulis secara sinkron ke node ZooKeeper. Karena ZooKeeper membatasi throughput penulisan metadata, batas aman praktis untuk kluster berbasis ZooKeeper adalah:

  • Maksimal 4.000 partisi per broker JVM.
  • Maksimal 200.000 partisi untuk keseluruhan kluster.

2. Kluster Berbasis KRaft (Apache Kafka 3.x / 4.x) #

Mode KRaft menyingkirkan ZooKeeper dan memindahkan manajemen metadata langsung ke dalam konsensus Raft internal Kafka. Karena KRaft mengonsolidasikan metadata ke dalam satu file log terdistribusi yang efisien, batas skalabilitas melonjak drastis:

  • Maksimal 10.000 hingga 20.000 partisi per broker JVM (tergantung pada spesifikasi RAM dan CPU).
  • Maksimal 1.000.000+ partisi untuk keseluruhan kluster.

Meskipun KRaft mampu menampung partisi dalam jumlah masif, kita tetap disarankan untuk tidak membuat partisi secara boros guna menjaga efisiensi penggunaan memori page cache.


Menghitung Skala Jumlah Broker dalam Kluster #

Setelah menentukan jumlah partisi, kita harus menghitung berapa banyak fisik node broker yang dibutuhkan untuk mendukung beban tersebut.

Langkah perhitungan skala broker dapat dirumuskan melalui dua kondisi batas berikut:

Batasan 1: Berdasarkan Kapasitas Throughput Hardware #

Kita harus membagi total throughput data kluster dengan kapasitas throughput aman yang mampu ditangani oleh satu unit server broker:

$$\text{Jumlah Broker (Throughput)} = \lceil \frac{\text{Total Throughput Kluster (Inbound + Outbound)}}{\text{Throughput Maksimum per Node (Hardware Limit)}} \rceil$$

Di mana:

  • Total Throughput Kluster: Gabungan seluruh inbound data (produser + replikasi) dan outbound data (konsumen).
  • Throughput Maksimum per Node: Batas performa terlemah antara bandwidth kartu jaringan (NIC) dan kecepatan tulis-baca disk pada satu server.

Batasan 2: Berdasarkan Toleransi Kegagalan (High Availability) #

Kluster Kafka harus memiliki node minimum yang cukup untuk mendistribusikan replika data secara aman. Jumlah broker tidak boleh lebih kecil dari faktor replikasi terbesar yang kita gunakan pada topik kluster:

$$\text{Jumlah Broker} \ge \text{Replication Factor (RF)}$$

Contoh Perhitungan Broker: #

  • Total Throughput Kluster: Diperkirakan mencapai $600 \text{ MB/s}$ saat lalu lintas puncak (inbound + outbound).
  • Spesifikasi Server: Setiap server broker dilengkapi dengan kartu jaringan $10 \text{ Gbps}$ ($\approx 1.000 \text{ MB/s}$ praktis) dan sistem disk SSD yang mampu melayani throughput stabil hingga $150 \text{ MB/s}$ untuk operasi tulis-baca campuran.
  • Faktor Replikasi (RF): Disetel 3 untuk seluruh topik penting.

Mari kita hitung jumlah broker yang dibutuhkan:

  1. Berdasarkan Throughput (hambatan terbesar adalah performa disk $150 \text{ MB/s}$): $$\text{Jumlah Broker} = \lceil \frac{600 \text{ MB/s}}{150 \text{ MB/s}} \rceil = 4 \text{ broker}$$
  2. Berdasarkan Kebutuhan Replikasi: $$\text{Jumlah Broker} \ge 3$$

Dengan membandingkan kedua kondisi di atas, kita harus menyediakan minimal 4 broker di dalam kluster. Untuk memberikan ruang cadangan aman (headroom) sebesar $25%$ guna mengantisipasi jika salah satu broker mati (N-1 redundancy), kita disarankan menambah 1 broker cadangan, sehingga total kluster produksi kita memiliki 5 broker.


Kepatuhan Operasional dan Checklist Audit Sizing Partisi & Broker #

Lakukan langkah audit berikut terhadap rencana desain kluster kita untuk memastikan alokasi partisi dan broker memenuhi kriteria stabilitas operasional:

No Item Kepatuhan Audit Sizing Metode Verifikasi Status
1 Partisi Seimbang Verifikasi bahwa jumlah partisi topik merupakan kelipatan dari jumlah broker aktif (misal 12 partisi untuk 3 broker) agar pembagian partisi merata. [ ]
2 Hindari Default Partisi 1 Pastikan parameter num.partitions pada server.properties disetel minimal 3 (bukan 1) sebagai standar topik baru. [ ]
3 Batas Partisi ZooKeeper Jika masih menggunakan ZooKeeper, pastikan total partisi yang ditampung per broker tidak melewati ambang batas 4.000 partisi. [ ]
4 Batas Partisi KRaft Jika menggunakan mode KRaft, pastikan total partisi per broker tidak melewati ambang batas aman 10.000 partisi. [ ]
5 File Descriptors Diperbesar Pastikan setelan OS /etc/security/limits.conf untuk user kafka telah menaikkan batas nofile minimal menjadi 100.000. [ ]
6 Toleransi Broker N-1 Pastikan kluster memiliki minimal satu server broker tambahan di atas kebutuhan throughput minimum untuk mengantisipasi failover. [ ]

Ringkasan #

  • Partisi untuk Paralelisme — Jumlah partisi menentukan jumlah maksimal instans konsumen aktif yang dapat memproses data secara bersamaan di dalam satu consumer group.
  • Gunakan Formula Throughput — Hitung jumlah partisi topik menggunakan rumus $\max\left(\frac{T}{p}, \frac{T}{c}\right)$ dengan membandingkan target throughput terhadap kecepatan thread produser dan konsumen.
  • Cegah Partition Bloat — Jangan membuat partisi berlebihan secara sembarangan untuk menghindari lambatnya pemulihan metadata, kehabisan file descriptor OS, dan tekanan memori JVM.
  • Sesuaikan Batas Mode Koordinasi — Batasi partisi maksimal 4.000 per broker pada mode ZooKeeper, dan manfaatkan skalabilitas mode KRaft hingga 10.000+ partisi per broker untuk kebutuhan berskala raksasa.

← Sebelumnya: Throughput Planning   Berikutnya: Performance Tuning →

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