Lompat ke konten
20 menit bacamateri 58 dari 96

Pertahankan urutan yang diperlukan

Partisi, consumer group, offset, rebalance, dan replay. Urutan global itu mahal, jadi tentukan urutan mana yang benar-benar dibutuhkan.

Ditinjau 26 Oktober 2026

Pertanyaan

Urutan pesan mana yang benar-benar harus dijaga, dan berapa paralelisme yang kita korbankan untuk itu?

Setelah baca ini, kamu bisa memilih partition key untuk sebuah alur, menjelaskan apa yang terjadi pada offset saat consumer crash atau rebalance, dan merencanakan replay tanpa merusak keadaan turunan.

Prasyarat dan contoh

Kamu sudah paham event stream sebagai log berurutan (M10.01), perilaku ack dan pengiriman ulang (M10.02), serta batas jumlah consumer (M10.03). Replikasi dan waktu logis dari M08 membantu.

Sebuah akun mengalami tiga event: saldo_masuk 100.000, saldo_keluar 80.000, saldo_keluar 50.000. Kalau dua event terakhir diproses terbalik, pembayaran 50.000 diperiksa dengan saldo 100.000 dan lolos, sedangkan urutan benar menolaknya. Tapi nggak ada gunanya menjaga urutan antara event akun A dan akun B. Urutan yang dibutuhkan adalah per akun, bukan global.

Mekanisme

Log berpartisi. Satu topik dibagi menjadi N partisi. Tiap partisi adalah log berurutan sendiri dengan nomor posisi (offset) yang naik. Urutan dijamin di dalam satu partisi, bukan antar partisi.

topik "saldo", 3 partisi
P0: [a1 a4 a7 ...]     key hash(akun) mod 3 = 0
P1: [b2 b5 ...]        key hash(akun) mod 3 = 1
P2: [c3 c6 ...]

Partition key. Producer memilih partisi dari key, misalnya hash(akun_id) mod N. Semua event satu akun masuk partisi yang sama, jadi urutannya terjaga, sementara akun berbeda diproses paralel. Memilih key = memilih unit urutan. Key yang terlalu kasar (misalnya negara) membuat paralelisme kecil dan partisi miring. Key yang terlalu halus (ID event acak) membuang urutan.

Consumer group. Beberapa consumer yang berbagi satu grup membagi partisi: satu partisi dimiliki satu consumer dalam grup pada satu waktu. Konsekuensinya: paralelisme maksimum grup sama dengan jumlah partisi. Dengan 12 partisi, consumer ke-13 menganggur. Grup berbeda mendapat salinan lengkap dan berjalan masing-masing (penagihan dan analitik membaca topik yang sama).

Offset dan commit. Consumer mencatat offset terakhir yang selesai diproses (commit). Setelah restart, ia lanjut dari offset itu. Ini versi stream dari ack, dengan semua trade-off yang sama:

  • Commit sebelum memproses: crash berarti event hilang.
  • Commit setelah memproses: crash berarti event terakhir diproses ulang. Butuh consumer idempoten (M10.02).

Karena commit dilakukan berkala, jendela pemrosesan ulang bisa mencakup puluhan event, bukan satu.

Rebalance. Saat consumer bergabung, keluar, atau dianggap mati (heartbeat terlambat), partisi dibagi ulang. Selama itu, partisi yang berpindah berhenti diproses sebentar, dan consumer baru lanjut dari offset terakhir yang di-commit. Dua bahaya: consumer lama yang lambat (bukan mati) masih memproses dan menulis efek setelah partisinya diambil orang lain, dan commit offset yang tertunda hilang sehingga event diproses ulang. Pakai idempotensi dan, untuk penulisan penting, fencing (nomor generasi) supaya penulis lama ditolak.

Replay. Karena log disimpan, consumer bisa mundur ke offset lebih awal atau ke waktu tertentu. Dipakai untuk membangun ulang tampilan turunan atau memperbaiki bug. Replay aman jika:

  1. Pemrosesan idempoten, atau tampilan turunan dihapus lalu dibangun dari awal.
  2. Efek keluar (email, tagihan) dimatikan atau dideduplikasi selama replay.
  3. Retensi log cukup. Kalau retensi 7 hari dan bug ditemukan hari ke-9, data pemicu sudah hilang.
  4. Kapasitas diperhitungkan: 7 hari data pada 5.000 event/detik berisi 3,02 miliar event. Kalau consumer replay hanya punya sisa 5.000 event/detik di atas beban normal, replay butuh 7 hari penuh. Pakai grup terpisah dengan kapasitas khusus.

Saat rancangan ini gagal

  • Key miring. Satu merchant besar menyumbang 40% event. Partisinya jenuh, partisi lain menganggur. Menambah consumer tidak membantu. Pilihan: pecah key (merchant#bucket) dan terima bahwa urutan hanya terjaga per bucket.
  • Menambah partisi mengubah pemetaan. Dari 12 ke 24 partisi, hash mod N berubah, jadi event baru akun A masuk partisi lain sementara event lamanya masih diproses. Urutan rusak sementara. Rencanakan jumlah partisi dari awal atau lakukan migrasi terkontrol.
  • Paralelisme di dalam consumer. Consumer membaca satu partisi lalu memproses event dengan thread pool. Urutan per key hilang di dalam proses. Pertahankan urutan per key di tingkat worker juga.
  • Retry di tengah aliran. Event 5 gagal dan dikirim ke DLQ atau antre ulang, sementara event 6 lolos. Urutan rusak. Keputusannya: hentikan partisi sampai 5 beres (aman, tapi memblokir), atau terima urutan longgar.
  • Rebalance badai. Pemrosesan satu pesan 40 detik, batas heartbeat 30 detik. Consumer dianggap mati, rebalance, pesan diproses ganda, dan ini berulang.
  • Replay menimpa data baru. Replay menulis nilai lama atas nilai lebih baru. Sertakan versi atau offset dalam tulisan dan tolak penulisan yang lebih tua.

Contoh tandingan: "pakai satu partisi biar urutannya pasti". Memang urut, tapi throughput terbatas pada satu consumer, mis. 2.000 event/detik, dan satu event lambat menahan semuanya.

Latihan

Topik pesanan berisi event dibuat, dibayar, dikirim per pesanan, 8 partisi, 8 consumer dalam satu grup, rata-rata 1.600 event/detik. Satu reseller besar menghasilkan 35% event.

  1. Pilih partition key dan jelaskan apa yang urutannya dijamin.
  2. Hitung beban partisi terbesar dan apa masalahnya.
  3. Consumer 3 crash setelah memproses 40 event sejak commit terakhir. Apa yang terjadi?
Lihat pembahasan
  1. Key pesanan_id. Semua event satu pesanan masuk satu partisi, jadi dibuat selalu sebelum dibayar. Urutan antar pesanan tidak dijamin dan memang tidak dibutuhkan.
  2. Kalau key reseller_id, partisi reseller besar menerima 35% × 1.600 = 560 event/detik, jauh di atas rata-rata 200/detik per partisi (1.600 / 8). Dengan key pesanan_id, beban relatif merata karena pesanan reseller tersebar. Itu alasan memilih pesanan_id. Kalau urutan per reseller dibutuhkan, itu syarat berbeda yang harus dibenarkan.
  3. Rebalance: partisi 3 dipindah ke consumer lain, yang lanjut dari offset commit terakhir. 40 event diproses ulang. Aman hanya jika consumer idempoten, misalnya mengabaikan dibayar untuk pesanan yang sudah berstatus dibayar. Selama rebalance partisi 3 tidak diproses beberapa detik, jadi lag-nya naik sementara.

Terkait

Catatan

  • Asumsi: broker bergaya log berpartisi. Istilah dan perilaku rebalance berbeda antar produk.
  • Hitungan replay: 7 × 86.400 × 5.000 = 3,024 × 10^9 event, dibagi sisa kapasitas 5.000/detik.
  • Yang nggak dibahas: transaksi lintas partisi, kompaksi log, dan urutan lintas wilayah.
  • Terakhir ditinjau: 26 Oktober 2026.

Isi · System Design