Episode ini membahas memodelkan insert, update, dan delete di sistem downstream, menangani event out-of-order dan konsumen idempotent, compaction, deduplikasi, dan pola upsert, serta membangun materialized views dan sistem CQRS.

Mengonsumsi event CDC adalah satu hal; memodelkannya dengan benar di sistem downstream adalah hal lain. Event dengan op yang sama bisa berarti hal berbeda tergantung state sistem penerima. Episode 18 ini membahas pola penanganan event: insert, update, delete, urutan yang kacau, duplikasi, dan cara membangun tampilan terkini dari aliran perubahan.
Inti masalahnya adalah ini: Kafka menjamin urutan per partisi, bukan at-least-once delivery. Artinya konsumen harus siap menghadapi event duplikat dan event yang sama dibaca lebih dari sekali. Semua pola di episode ini berakar pada kenyataan tersebut.
Setiap op memiliki makna dan aksi yang berbeda:
c — insert: tambahkan record baru.u — update: timpa record yang ada.d — delete: hapus record, dan perhatikan tombstone.r — read: hasil snapshot, perlakukan seperti upsert.Untuk delete, Debezium mengirim dua event: yang pertama value berisi op: d dengan before, lalu tombstone dengan value null pada key yang sama. Tombstone diperlukan untuk mendukung kompaksi topic. Konsumen harus siap menerima value null dan tidak crash saat itu terjadi.
docker exec -it kafka /opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server localhost:9092 \
--topic dbserver1.inventory.customers \
--property print.value=falseKonsumen yang benar memperlakukan op: d sebagai hapus, lalu mengabaikan tombstone null yang menyusul.
Karena konsumen bisa membaca event lebih dari sekali, operasi tulis harus idempotent: menjalankan operasi yang sama dua kali memberi hasil yang sama. Dua strategi dasar:
INSERT ... ON CONFLICT ... DO UPDATE sehingga update ganda aman.source.ts_ms atau posisi log di record target dan tolak event yang lebih lama.Untuk event yang datang tidak berurutan lintas partisi, kunci perbandingan source.ts_ms menentukan urutan logis. Sink JDBC dengan insert.mode: upsert dan pk.fields otomatis memenuhi prinsip idempotensi ini.
Log compaction adalah fitur Kafka yang menjaga nilai terakhir untuk setiap key. Aktifkan pada topic yang mewakili state, bukan aliran kejadian:
docker exec -it kafka /opt/kafka/bin/kafka-topics.sh \
--bootstrap-server localhost:9092 \
--alter --topic dbserver1.inventory.customers \
--config cleanup.policy=compactDengan cleanup.policy=compact, Kafka menyimpan nilai terbaru per key dan membuang yang lama saat segmen di-compact. Tombstone yang dikirim Debezium ikut menghapus key saat compaction, sehingga state tersimpan akurat.
Gabungkan compaction dengan pola upsert untuk membangun state store yang selalu sinkron dengan database sumber.
Materialized view adalah representasi state yang diperbarui terus menerus dari event. Ada dua pendekatan:
CREATE TABLE customer_state AS
SELECT id, LATEST_BY_OFFSET(first_name) AS first_name,
LATEST_BY_OFFSET(email) AS email
FROM customers_stream
GROUP BY id
EMIT CHANGES;Tabel customer_state menampilkan state terakhir setiap customer. Pola ini juga menjadi dasar CQRS: sisi command menulis ke database sumber, event CDC mengalir, dan sisi query membaca dari materialized view — pemisahan beban baca dan tulis yang bersih.
Saat incremental snapshot dijalankan ulang (episode 4), konsumen akan menerima kembali event op: r untuk data yang sudah ada. Karena alasan inilah konsumen harus memperlakukan event snapshot seperti upsert, bukan insert polos. Menerapkan op: r dengan INSERT ... ON CONFLICT DO UPDATE menjaga state tetap konsisten tanpa duplikasi.
INSERT INTO customers (id, first_name, email)
VALUES (?, ?, ?)
ON CONFLICT (id) DO UPDATE
SET first_name = EXCLUDED.first_name,
email = EXCLUDED.email;Query ON CONFLICT (id) DO UPDATE di atas bekerja untuk event c, u, dan r sekaligus. Dengan satu bentuk penulisan, konsumen tidak perlu memilah-milah jenis operasi untuk aksi tulis.
Pola penanganan event tidak berguna jika tidak diuji. Latihan yang disarankan:
Jadikan skenario ini bagian dari test pipeline sehingga perubahan kode tidak merusak perilaku yang sudah benar.
Episode 18 membahas pola penanganan event yang benar: memetakan op ke aksi downstream, menghadapi tombstone dan duplikasi dengan konsumen idempotent, memakai compaction untuk state, dan membangun materialized views serta CQRS dari aliran perubahan.
Inti yang harus dibawa pulang:
null menyertai delete dan dibutuhkan untuk kompaksi.Di episode 19 selanjutnya kita akan membahas operational readiness dan runbooks — menyusun runbook untuk kegagalan connector, snapshot restart, dan data replay, incident response untuk lag dan schema issues, serta backup konfigurasi dan offset.