Real-time analytics pipeline adalah jalur yang membawa tiap kejadian di aplikasi sampai jadi angka di dashboard dalam hitungan detik, bukan besok pagi. Bayangin papan skor di GOR waktu pertandingan bulutangkis. Tiap poin langsung muncul di papan, karena ada petugas yang menonton lapangan dan menambah angkanya saat itu juga. Rekap resmi pertandingan baru keluar setelah selesai. Kalau petugas papan skor ragu apakah poin tadi sudah dia catat, lalu mencatatnya lagi, skornya salah di depan semua penonton. Pipeline real-time adalah petugas papan skor itu, dan pelajaran ini membahas cara supaya dia gak mencatat dua kali.
🧪 Tebakan cepat: Sabtu 10:15:30 Siti membeli 1 Bumi Manusia (pesanan 15). Consumer menyimpan event-nya ke tabel dashboard, lalu mati sebelum sempat commit offset Kafka. Setelah hidup lagi, "Terjual hari ini" bilang 1 atau 2? Tebak dulu, jawabannya ada di bagian "At-least-once dan sink yang idempoten".
Dari checkout ke dashboard
Pertanyaan pemilik kali ini beda: bukan "paling laku per minggu", tapi "berapa buku terjual hari ini, sekarang?". Jalurnya menyambung pelajaran-pelajaran sebelumnya:
| Tahap | Tugasnya | Di Toko Buku |
|---|---|---|
| Sumber | aplikasi menulis pesanan | MySQL, tabel order_items |
| CDC | membaca tiap perubahan dari binlog | Debezium 3.7 (pelajaran CDC) |
| Broker | menyimpan event berurutan | topic Kafka tokobuku.toko_buku.order_items (pelajaran Kafka) |
| Stream processor | menghitung terus-menerus | consumer kecil, atau Flink, Kafka Streams, RisingWave, Materialize |
| Serving store | tempat dashboard bertanya | Postgres; untuk data besar ClickHouse, Druid, Pinot |
| Dashboard | menampilkan angkanya | Metabase, Superset, Grafana |
Di Lab, 22 event mengalir lewat jalur ini: 19 baris pesanan (op = c, insert), lalu tiga perubahan hari Sabtu dari pelajaran CDC: item 9 yang dobel dihapus jam 09:00 (d), harga item 16 dibetulkan (u), dan item 20 milik Siti masuk jam 10:15:30 (c).
Stream processor: siapa yang menghitung
Stream processor adalah program yang nyala terus, membaca event begitu datang, dan memperbarui hasil hitungannya. Pilihan yang dipakai orang per Oktober 2026:
- Apache Flink (2.3, Juni 2026): mesin stream processing terpisah dengan cluster sendiri. Menyimpan state (hitungan sementara) dan membuat checkpoint berkala. Bisa ditulis dengan Java, Python, atau Flink SQL.
- Kafka Streams: library Java yang ikut di Apache Kafka (4.3.1). Jalan di dalam aplikasimu sendiri, gak butuh cluster lain, tapi cuma membaca dan menulis Kafka.
- RisingWave (3.1.0, 21 September 2026, lisensi Apache 2.0) dan Materialize (v26.45.1, 8 Oktober 2026, Business Source License: boleh dijalankan sendiri di produksi selama total memori di bawah 24 GiB per instalasi, atau pakai versi cloud-nya): database streaming yang bicara dialek PostgreSQL. Kamu menulis
CREATE MATERIALIZED VIEW, dan hasilnya diperbarui tiap ada event.
-- RisingWave atau Materialize: order_items di sini tabel yang diisi dari CDC
CREATE MATERIALIZED VIEW terjual_harian AS
SELECT CAST(created_at AS DATE) AS tanggal, SUM(qty) AS buku
FROM order_items
GROUP BY CAST(created_at AS DATE);
Di PostgreSQL biasa, materialized view yang sama baru berubah kalau kamu menjalankan REFRESH MATERIALIZED VIEW. Di database streaming, insert, update, dan delete dari CDC langsung mengubah angkanya.
Pilihan paling sederhana, dan yang dimodelkan Lab: consumer kecil (misalnya Python dengan kafka-python dari pelajaran Kafka) yang menulis tiap event ke tabel Postgres, lalu dashboard menjalankan SELECT.
Jendela menurut waktu pesanan
Stream gak pernah selesai, jadi "jumlahkan semuanya" gak bisa. Windowing artinya mengelompokkan event menurut rentang waktu (tiap 5 menit, tiap jam, tiap hari) lalu menjumlah per rentang itu. "Hari ini" adalah jendela tumbling 1 hari dari pelajaran Streaming vs Batch: potongan tetap, tiap pesanan masuk tepat satu hari. Yang menentukan harinya adalah event time, yaitu created_at pesanan, bukan jam event sampai di consumer. Hasil dashboard setelah 22 event, dari SQLite di Lab:
| tanggal | buku |
|---|---|
| 2026-09-28 | 3 |
| 2026-10-01 | 3 |
| 2026-10-09 | 1 |
| 2026-10-10 | 1 |
(8 hari lain dipendekkan; 3 Oktober gak muncul karena gak ada penjualan.) Sebelum Sabtu, 1 Oktober tertulis 5: item 9 yang dobel ikut terhitung. Event hapus jam 09:00 hari Sabtu membetulkan jendela 1 Oktober, bukan jendela Sabtu, karena barisnya milik pesanan 1 Oktober. Stream dari CDC bukan cuma tambahan: ada update dan delete yang mengubah jendela lama.
Jendela lain dari pelajaran itu tetap berlaku. Sliding 24 jam terakhir pada Sabtu 10:16 berisi 2 buku (pesanan 14 hari Jumat 10:30 masih masuk). Dan event yang datang telat tetap masalah: di pelajaran Streaming vs Batch, minggu 28 September keluar 12 buku tanpa waktu tunggu watermark, 13 dengan waktu tunggu 12 jam.
Serving store: tempat dashboard bertanya
- Postgres: cukup untuk ribuan sampai jutaan baris per hari. Tabel kecil dengan primary key, dashboard menjalankan
SELECT. Ini pilihan Toko Buku. - ClickHouse (26.9, rilis stabil Oktober 2026; 26.8 versi LTS), Apache Druid (38.0.0, 1 Oktober 2026), Apache Pinot (1.5.1, 5 Juni 2026): database kolom untuk analitik real-time. Ketiganya bisa membaca langsung dari Kafka dan menjawab agregasi atas miliaran baris dalam waktu singkat. Dipakai untuk dashboard yang dibuka ribuan orang sekaligus, misalnya statistik penjual di marketplace.
At-least-once dan sink yang idempoten
Consumer di Lab commit offset tiap 5 event. Setelah event ke-22 (offset 21), commit terakhir ada di offset 19. Kalau consumer mati di sini, penggantinya membaca ulang offset 20 dan 21. Ini at-least-once dari pelajaran Kafka: event bisa datang dua kali, gak ada yang hilang. Yang menentukan hasilnya adalah sink, cara consumer menulis ke serving store.
Sink yang menambah:
INSERT INTO terjual_harian (tanggal, buku) VALUES ('2026-10-10', 1)
ON CONFLICT (tanggal) DO UPDATE SET buku = buku + excluded.buku;
Sink yang menulis satu baris per item (upsert), lalu dashboard menjumlah:
INSERT INTO item_live (item_id, tanggal, qty) VALUES (20, '2026-10-10', 1)
ON CONFLICT (item_id) DO UPDATE SET tanggal = excluded.tanggal, qty = excluded.qty;
-- event d: DELETE FROM item_live WHERE item_id = 9;
SELECT tanggal, SUM(qty) AS buku FROM item_live GROUP BY tanggal ORDER BY tanggal;
ON CONFLICT ... DO UPDATEsama di PostgreSQL dan SQLite.excludedadalah baris yang barusan mau disisipkan.- Sink yang menambah gak tahu event mana yang sudah pernah dia hitung. Event yang sama dua kali = ditambah dua kali.
- Upsert per
item_ididempoten: menulis event yang sama dua kali memberi hasil yang sama.
Hasil setelah consumer mati dan event offset 20 dan 21 dibaca ulang: sink yang menambah bilang 2 buku hari ini, upsert bilang 1. Itu jawaban tebakannya: tergantung sink-nya, dan yang benar cuma yang idempoten.
Exactly-once artinya tiap event berpengaruh tepat sekali. Flink mencapainya di dalam dirinya dengan checkpoint: state dan offset Kafka disimpan bersama, dan setelah crash keduanya dikembalikan bersama. Kafka Streams punya processing.guarantee=exactly_once_v2, tapi itu berlaku untuk tulis ke Kafka sendiri. Begitu hasilnya ditulis ke database lain, baris yang sudah masuk sesudah checkpoint terakhir bisa tertulis lagi. Jadi sampai ke dashboard, exactly-once butuh sink transaksional atau sink yang idempoten. Yang kedua jauh lebih sederhana.
Biaya, dan apakah Toko Buku butuh ini
Jujur: belum. Toko Buku punya 1 sampai 3 pesanan sehari. Jalur di atas berarti Kafka, Kafka Connect dengan Debezium, dan prosesor yang semuanya nyala 24 jam, dimonitor, di-upgrade, dan dibayar tiap bulan, untuk angka yang berubah tiga kali sehari. Pertanyaan pemilik bisa dijawab dengan satu query ke read replica (salinan database yang khusus dibaca), dijalankan dashboard tiap beberapa menit:
SELECT COALESCE(SUM(oi.qty), 0) AS terjual_hari_ini
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
WHERE o.created_at >= CURDATE();
Real-time layak kalau ada keputusan yang harus diambil dalam detik: menahan pembayaran yang mencurigakan, mengunci stok saat flash sale, membunyikan alarm saat checkout gagal beruntun. Atau kalau query langsung ke database sudah terlalu berat karena datanya besar.
⚠️ Jebakan yang sering ketemu
- Jendela menurut processing time. Event yang tertahan di antrean masuk ke hari yang salah. Hitung dengan
created_atpesanan. - Sink yang menambah tanpa kunci. At-least-once pasti kadang mengirim ulang, dan
buku = buku + qtymenghitungnya dua kali. Tulis per kunci unik. - Menganggap stream cuma insert. CDC juga mengirim update dan delete. Consumer yang mengabaikan
op=dmembiarkan item 9 terhitung selamanya. - Membangun real-time sebelum ada yang butuh. Mulai dari query ke replica; pindah ke streaming kalau keputusannya memang butuh detik.
TL;DR: Pipeline real-time Toko Buku: MySQL, Debezium (CDC), Kafka, stream processor, serving store, dashboard. Prosesornya bisa consumer kecil, Flink 2.3 (state + checkpoint), Kafka Streams (library di Kafka 4.3), atau database streaming RisingWave 3.1 dan Materialize yang memperbarui CREATE MATERIALIZED VIEW tiap event. Serving store-nya Postgres untuk data kecil, ClickHouse, Druid, atau Pinot untuk data besar. "Hari ini" adalah jendela tumbling 1 hari menurut waktu pesanan (event time), dan event d membetulkan jendela lamanya (1 Oktober dari 5 jadi 3). Kafka memberi at-least-once, jadi event bisa datang dua kali: sink yang menambah bilang 2, upsert per item_id yang idempoten tetap 1. Exactly-once Flink berlaku di dalam Flink; sampai ke database perlu sink idempoten atau transaksional. Untuk 1 sampai 3 pesanan sehari, query ke read replica tiap beberapa menit sudah cukup.