Kafka dan Redis Streams sama-sama menyimpan urutan event dan mendukung consumer group. Kemiripan itu nggak menjelaskan gimana masing-masing sistem menangani consumer yang crash, ordering, atau data yang saling terkait lintas stream. Saya mau membandingkan perilaku-perilaku itu lewat aplikasi yang beneran jalan.
Saya membangun sistem yang sama dua kali sebagai proyek pribadi, sekali dengan Kafka dan sekali dengan Redis Streams. Keduanya pakai broker yang jalan di Docker tanpa mock. Seri ini mencatat apa yang saya pelajari dari implementasi-implementasi itu.
Kenapa membandingkan dua sistem dengan tugas yang berbeda
Kedua sistem sama-sama menyediakan log dan consumer group, tapi model kepemilikan dan recovery-nya beda. Kosakata yang sama bisa menyembunyikan perbedaan ini. Perbandingan yang berguna perlu mengidentifikasi siapa yang menerima tiap record dan apa yang terjadi kalau pemrosesannya gagal.
Saya membangun sistem yang sama dengan kedua broker untuk membandingkan perilakunya. Pipeline-nya butuh pemrosesan yang terurut, recovery dari kegagalan, dan join antara dua aliran data.
Contoh yang dipakai: ticker dengan price alert
Ticker ini melacak 10 simbol IDX, termasuk BBCA dan BBRI. Harga sintetis dengan seed bikin tiap run bisa diulang. Dua channel membawa traffic-nya:
- market updates: firehose price tick, satu per perubahan simbol, dengan simbol sebagai key, 6 partition di sisi Kafka.
- notifications: price alert pakai perbandingan seperti
<,<=,=,>=, dan>. Notification pakai simbol sebagai key supaya sejajar dengan partition price tick.
Kedua channel pakai key simbol dan jumlah partition yang sama. Ini memungkinkan nomor partition yang cocok lintas topic. Kalau satu consumer memiliki partition p dari market-updates dan notifications sekaligus, dia bisa menyimpan state yang terkait secara lokal. State lokal itu tetap butuh inisialisasi sebelum sebuah notification bisa memakainya.
graph LR
S["Price Simulator"] -->|"key: symbol"| MU["market-updates (6 partitions)"]
MU --> AE["Alert Evaluator"]
AE -->|"price crosses alert"| N["notifications (co-partitioned)"]
N --> D["Live Dashboard (SSE)"]
Sebuah simulator mem-publish tick untuk ke-10 simbol. Alert evaluator menyimpan harga sebelumnya untuk mendeteksi persilangan threshold. Untuk alert di 9.000, dia harus memberi notifikasi sekali waktu BBCA naik melewati 9.000. Tick berikutnya yang masih di atas threshold nggak boleh mengulang alert persilangan itu. Dashboard menerima notification lewat Server Sent Events.
Bangun ulang pipeline yang sama di Redis Streams dan bentuknya tetap identik. Cuma mekanisme di bawahnya yang berubah, dan mekanisme itulah yang mau saya bandingkan.
Dua log yang berbeda
Kafka menyimpan record topic di partition yang terurut di disk. Cluster opsional saya pakai replication factor 3, dengan salinan partition di broker yang berbeda. Di dalam satu consumer group, tiap partition cuma punya satu pemilik dalam satu waktu.
Key yang stabil menjaga record BBCA tetap di satu partition. Consumer membaca record-record itu sesuai urutan append. Setting replication dan acknowledgment menentukan gimana log menangani kegagalan broker.
Redis Streams menyimpan entry di memori dengan representasi radix tree. Entry ID yang di-generate menggabungkan komponen milidetik dan sequence number. Persistence AOF atau RDB itu opsional.
Satu stream adalah satu sequence yang terurut. Saya membuat beberapa stream kalau butuh sharding dan pakai hash tag untuk key yang saling terkait. Anggota consumer group berebut entry, jadi pemrosesan yang konkuren nggak menjaga urutan selesainya lintas consumer.
Kafka menyediakan log yang dipartisi, replication, dan posisi consumer yang independen. Redis Streams menambahkan log terurut ke Redis. Tim yang udah mengoperasikan Redis bisa memakai ulang pengalaman itu, sambil tetap mengecek kebutuhan persistence, kapasitas, dan recovery.
Kenapa saya nggak nge-mock apa pun
Saya menjalankan Kafka dalam mode KRaft dan Redis di Docker. Pertama, saya memastikan tiap sistem bisa mem-produce dan meng-consume sebuah message.
Saya pakai program KafkaJS dan ioredis yang langsung ke broker, simulasi di browser, dan dashboard SSE. Saya juga mengimplementasikan murmur2 dan CRC16 di TypeScript untuk membandingkan prediksi routing dengan perilaku broker. Browser dan test harness berbagi hash function yang sama.
Saya mau mengamati lag dan recovery selama terjadi kegagalan. Menghentikan sebuah container bikin saya bisa melihat group melakukan rebalance dan memproses backlog yang tersisa.
Selanjutnya ke mana
Dalam beberapa minggu ke depan saya mau menuliskan bagian-bagiannya, kurang lebih sesuai urutan saya menemuinya.
Seri ini dimulai dengan key dan partition di Kafka, lalu command Redis seperti XADD dan MAXLEN. Seri ini juga membahas entry ID di Redis. Group di Kafka membagikan partition ke consumer. Group di Redis mendistribusikan entry dan melacak entry yang butuh acknowledgement.
Sebagian besar waktu saya habis di kegagalan: timing commit, retry, dead letter, dan transaction. Saya juga membandingkan delivery Pub/Sub dengan stream yang disimpan. Bagian yang paling saya nikmati adalah custom assigner KafkaJS. Assigner itu menjaga partition yang cocok tetap bersama supaya ticker bisa men-join datanya secara lokal.
Menjelang akhir, separuh yang operasional. Model in sync replica di Kafka dibandingkan dengan sharding hash slot di Redis Cluster, termasuk apa yang saya lihat waktu saya mematikan leader broker di tengah run. Stateful processing dengan alert edge versus level dan window OHLC. Dashboard-nya sendiri. Benchmark yang dijalankan dengan cara yang sama untuk keduanya supaya angkanya bisa dibandingkan. Lalu pertanyaan yang semua orang tanyakan duluan, dijawab paling akhir: Kafka atau Redis Streams, gimana cara memilihnya.
Setiap klaim di seri ini berasal dari sesuatu yang saya jalankan, saya lihat rusak, lalu saya jalankan lagi.
Saya mulai dari mekanisme yang jadi fondasi seluruh Kafka: key, murmur2, dan gimana enam partition menjaga setiap simbol tetap berurutan.

Memuat komentar...