A detailed backend reference

Kafka, log di jantung sistem Anda.

Versi Sederhana

Penelusuran dari prinsip dasar tentang platform event-streaming terdistribusi yang menggerakkan sistem saraf LinkedIn, Uber, Netflix, dan kebanyakan fintech modern: dari satu gagasan yang menopang segalanya (commit log append-only), lewat partisi, consumer group, offset dan semantik pengiriman, sampai retensi, skema, pola event-driven, trik outbox/CDC, dan stream processing. Ditulis bukan hanya menyebut apa fungsi tiap bagian tapi kenapa ia ada dan bagaimana ia bekerja di dalamnya. Kode producer/consumer di Go dan Python.

Distributed commit log Partitions & consumer groups Go 1.22+ / Python 3.11+ 21 sections
Part I / The Log Model
01

Apa Sebenarnya Kafka Itu

Buang pemasaran dan Kafka satu hal: log peristiwa terdistribusi, append-only, tahan lama yang dibaca banyak consumer independen dengan kecepatan mereka sendiri. Bukan antrean, bukan database, bukan message bus dalam arti klasik, sebuah log. Producer menambahkan record ke ujung; consumer membaca maju dari posisi yang mereka lacak sendiri; dan record tetap setelah dibaca, selama retensi terkonfigurasi. Begitu satu gambar itu masuk kepala Anda, setiap fitur di manual ini konsekuensinya.

Deskripsi resminya "platform event streaming terdistribusi," dan tiap kata layak tempatnya. Distributed: ia berjalan sebagai klaster broker, mereplikasi data demi toleransi kegagalan dan scaling horizontal. Event streaming: ia dibangun di sekitar aliran event immutable yang tak terbatas dan berkelanjutan (sesuatu terjadi: order ditempatkan, pembayaran jelas, sensor membaca), bukan panggilan request/response. Platform: producer, consumer, ekosistem konektor, dan stream processing semuanya duduk di log yang sama.

A traditional message queue is a to-do list: you take an item, do it, and it's gone. Kafka is a ship's logbook: every event is written down in order and stays in the book. Ten different officers can each read the log from wherever they left off, re-read yesterday's entries, or start from the beginning, and reading never erases a line.
The shape of Kafka
Producers append; the log persists; many consumers read independently
producers append -> THE LOG (append-only, retained) e0 e1 e2 e3 e4 ... offset 0    1     2     3     4 -> new writes go to the tail consumer A @ offset 4 consumer B @ offset 1 consumer C @ offset 0 Each consumer tracks its own position; reading e1 doesn't remove it, another consumer can still read it.
This decoupling, durable retained events plus per-consumer positions, is the whole game. It's what lets one stream of events feed many unrelated systems independently.
The one definition

Kafka commit log terdistribusi, tahan lama, append-only berisi event immutable, dipartisi untuk skala dan direplikasi untuk toleransi kegagalan, yang dibaca banyak consumer independen di offset mereka sendiri: dengan event dipertahankan setelah dibaca. Segala hal lain detail di atas itu.

02

Antrean vs Log: Kenapa Kafka Berbeda

Ini bagian terpenting di manual, karena mencampurkan Kafka dengan message queue tradisional akar sebagian besar kebingungan. Bab task-queues mencakup model antrean secara mendalam, producer, worker, broker seperti RabbitMQ dan SQS, retry, visibility timeout. Kafka jenis benda yang berbeda, dan perbedaannya bukan detail; ia mengubah masalah apa yang dipecahkan.

The fundamental contrast
A queue distributes & deletes; a log retains & replays
QUEUE (RabbitMQ, SQS), competing consumers msg in m3m2m1 worker 1 worker 2 each message goes to ONE worker & is DELETED once acked no replay / order across consumers lost LOG (Kafka), independent consumers e0e1e2e3 retained, nothing deleted on read billing reads all analytics reads all search reads all every consumer sees EVERY event / can replay from any offset
DimensiAntrean tradisional (bab 10)Log Kafka
Setelah pesan dibacaDihapus (dikonsumsi sekali)Dipertahankan (sampai retensi kedaluwarsa)
Siapa yang menerima pesanSatu consumer yang bersaingSetiap consumer group, independen
Replay / baca ulangTidak, sudah hilangYa, seek ke offset mana pun
Posisi consumerDilacak & dihapus brokerConsumer melacak offsetnya sendiri
UrutanLemah/tidak ada lintas consumerKetat dalam satu partisi
Model mentalDistribusi kerja (daftar tugas)Riwayat peristiwa / sumber kebenaran (logbook)
Titik manisOffloading tugas, pemrosesan jobEvent streaming, banyak subscriber, replay, throughput tinggi
They coexist, this isn't either/or

Anda sering memakai keduanya: Kafka sebagai tulang punggung event tahan lama (order, pembayaran, klik di-stream ke banyak sistem), dan task queue (Celery/SQS, bab 10) untuk job latar diskret (kirim email ini, resize gambar itu). Perbandingan penuh dan "yang mana kapan" di bagian 17. Untuk sekarang pegang bedanya: antrean = distribusikan kerja dan lupakan; log = catat event dan biarkan banyak sistem mengonsumsi & memutar ulangnya.

03

Commit Log

Semua yang Kafka lakukan bertumpu pada struktur data paling rendah hati dalam komputasi: log append-only: urutan record terurut yang hanya bisa Anda tambahkan di ujung, tidak pernah menyisipkan di tengah atau memodifikasi. Tiap record mendapat offset yang naik monoton (0, 1, 2, ...) yang menjadi alamat permanennya. Ini struktur yang sama yang dipakai database untuk write-ahead log dan daya tahannya; wawasan Kafka menjadikan log itu sendiri abstraksi primer yang publik alih-alih detail internal tersembunyi.

Append-only, offset-addressed
Writes go to the tail; the offset is a permanent address
rec rec rec rec rec new 012345 append only at the tail a reader advances forward by offset; it never goes back unless you seek No updates, no deletes-in-place, no random inserts, just append and read. That constraint is the superpower.

Kenapa log append-only begitu cepat dan begitu berguna

  • I/O sekuensial murah. Menambahkan ke ujung file dan membacanya maju operasi disk sekuensial, jauh lebih cepat daripada akses acak, bahkan di SSD. Kafka bersandar pada OS page cache dan transfer zero-copy untuk memindahkan data dari disk ke jaringan tanpa melewati memori aplikasi, begitulah ia menopang jutaan pesan per detik di perangkat keras sederhana.
  • Immutabilitas sederhana & aman. Record tidak pernah berubah, jadi tidak ada lock untuk update, tidak ada konflik baca/tulis, dan pembaca serentak tidak pernah mengganggu. Riwayat immutable juga sepele di-cache dan mudah dinalar.
  • Log adalah riwayatnya. Karena tidak ada yang ditimpa, log catatan lengkap terurut dari semua yang terjadi, persis yang membuat replay (bagian 11), event sourcing (bagian 14), dan memberi makan consumer baru dari awal mungkin.
One structure, everything follows

Pegang log append-only dengan mantap: tulis menambahkan dan mendapat offset; baca bergerak maju; record immutable dan dipertahankan. Partisi (bagian 4) hanya banyak log seperti itu; consumer group (bagian 7) penanda buku di dalamnya; semantik pengiriman (bagian 9) soal seberapa hati-hati Anda melacak penanda itu. Seluruh sistem gagasan log, di-scaling dan direplikasi.

Part II / Architecture
04

Topik & Partisi

Topic aliran bernama dari event terkait, orders, payments, page-views. Ia kanal logis yang ditulis producer dan dibaca consumer. Tapi topic bukan satu log; ia dipecah menjadi partisi, dan partisi tempat semua mekanik nyata hidup. Partisi sekaligus unit Kafka untuk paralelisme, urutan, dan penyimpanan, tiga properti terpenting dalam sistem, semuanya menumpang satu konsep ini.

A topic is partitioned
One topic, several independent append-only logs; a key picks the partition
producerkey -> hash TOPIC: orders (3 partitions) P0 P1 P2 Ordering is guaranteed WITHIN a partition, not across them. Records with the same key always land on the same partition, so all events for one order stay in order. partition = hash(key) % numPartitions

Tiga peran partisi

  • Paralelisme. Partisi bisa hidup di broker berbeda dan dikonsumsi consumer berbeda sekaligus, jadi throughput topic berskala dengan jumlah partisinya. Lebih banyak partisi -> lebih banyak konsumsi paralel (sampai batas consumer group, bagian 7).
  • Urutan. Kafka menjamin urutan dalam satu partisi dan tidak berjanji apa pun lintas partisi. Ini kompromi sentral (bagian 10): untuk menjaga event terkait terurut, rutekan mereka ke partisi yang sama via key bersama (semua event untuk order-42 memakai key "order-42", jadi mereka terurut ketat relatif satu sama lain).
  • Penyimpanan. Setiap partisi secara fisik satu set file segmen di disk broker, log append-only aktual dari bagian 3. Retensi dan kompaksi (bagian 11) beroperasi per partisi.
Partition count is a one-way-ish decision

Anda bisa menaikkan partisi topic belakangan, tapi melakukannya memecah stabilitas key-ke-partisi (modulo berubah, jadi key yang ada bisa pindah) dan karenanya memecah jaminan urutan untuk key yang sedang berjalan. Dan Anda tidak bisa dengan mudah menurunkannya. Jadi pilih jumlah partisi sengaja di depan berdasarkan target throughput dan paralelisme consumer maksimum yang akan Anda butuhkan, terlalu sedikit membatasi scaling Anda, terlalu banyak menambah overhead (bagian 19, bagian 20).

05

Broker, Replikasi & Daya Tahan

Klaster Kafka satu set broker (server); partisi tersebar di antara mereka, dan begitulah klaster berskala dan selamat dari kegagalan. Daya tahan datang dari replikasi: tiap partisi punya satu replika leader dan beberapa replika follower di broker lain. Semua baca dan tulis pergi ke leader; follower terus menyalin darinya. Jika broker leader mati, follower dipromosikan, tanpa data hilang, ketidaktersediaan singkat. Ini pola pikir desired-state/failover yang sama dengan bab Kubernetes, diterapkan ke data.

Replication
Each partition has a leader and in-sync followers on other brokers
Broker 1 P0 (leader) P1 (follower) P2 (follower) Broker 2 P0 (follower) P1 (leader) P2 (follower) Broker 3 P0 (follower) P1 (follower) P2 (leader) Leaders are spread across brokers for balance; each follower replicates its leader. Lose a broker -> its leaders' followers take over.

Kenop daya tahan: acks & ISR

Seberapa aman sebuah tulis bergantung dua setelan. acks producer menyebut berapa replika yang harus mengonfirmasi sebelum tulis dianggap selesai: acks=0 (fire-and-forget, bisa kehilangan data), acks=1 (leader saja, hilang jika leader mati sebelum follower menyalin), atau acks=all (leader plus semua in-sync replica: pilihan aman). ISR (in-sync replica set) follower yang saat ini mengejar leader; memasangkan acks=all dengan min.insync.replicas=2 berarti tulis hanya sukses jika setidaknya dua replika memilikinya, selamat dari satu kehilangan broker dengan nol kehilangan data.

SetelanJaminanKompromi
acks=0Tidak ada, fire and forgetTercepat; bisa diam-diam kehilangan data
acks=1Leader memilikinyaCepat; hilang jika leader gagal sebelum replikasi
acks=all + min.insync.replicas=2>=2 replika memilikinya sebelum suksesPaling aman; latensi sedikit lebih tinggi, standar untuk data penting
ZooKeeper is gone, KRaft

Kafka lama mengandalkan klaster ZooKeeper terpisah untuk menyimpan metadata dan memilih leader. Kafka modern memakai KRaft (Kafka Raft), melipat peran itu ke broker itu sendiri via quorum controller berbasis Raft, lebih sedikit bagian yang bergerak, failover lebih cepat, ops lebih sederhana. Jika Anda membaca dokumen lama menyebut ZooKeeper, KRaft penggantinya.

06

Producer

Producer menerbitkan record ke topic. Tiga perhatian producer membentuk kebenaran dan performa: key (yang menentukan partisi dan karenanya urutan, bagian 4), acks (daya tahan, bagian 5), dan batching (producer mengelompokkan record dan mengompresnya untuk throughput). Record pasangan key/value plus header opsional dan timestamp; value payload event Anda (sering Avro/Protobuf, bagian 12).

// Using confluent-kafka-go (librdkafka). kafka-go (segmentio) is a pure-Go alternative.
p, _ := kafka.NewProducer(&kafka.ConfigMap{
    "bootstrap.servers": "localhost:9092",
    "acks":              "all",   // durability: leader + in-sync replicas (sec 5)
    "enable.idempotence": true,   // no duplicates on producer retry (sec 9)
    "linger.ms":         5,       // wait up to 5ms to batch records (throughput)
    "compression.type":  "zstd",
})
defer p.Close()

topic := "orders"
// The KEY decides the partition -> all events for one order stay ordered (sec 4, sec 10).
err := p.Produce(&kafka.Message{
    TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},
    Key:            []byte("order-42"),
    Value:          []byte(`{"event":"placed","amount":1999}`),
}, nil)
if err != nil { log.Fatal(err) }

// Producing is async + batched; Flush blocks until queued messages are delivered.
p.Flush(5000)

// Delivery reports tell you the final partition/offset (or an error) per message:
//   for e := range p.Events() { if m, ok := e.(*kafka.Message); ok { ... } }
# Using confluent-kafka (librdkafka). kafka-python / aiokafka are alternatives.
from confluent_kafka import Producer

p = Producer({
    "bootstrap.servers": "localhost:9092",
    "acks": "all",                 # durability: leader + in-sync replicas (sec 5)
    "enable.idempotence": True,    # no duplicates on producer retry (sec 9)
    "linger.ms": 5,                # batch window for throughput
    "compression.type": "zstd",
})

def on_delivery(err, msg):         # async callback: final partition/offset or error
    if err:
        print("delivery failed:", err)
    else:
        print(f"delivered to {msg.topic()}[{msg.partition()}]@{msg.offset()}")

# The KEY decides the partition -> all events for one order stay ordered (sec 4, sec 10).
p.produce(
    "orders",
    key=b"order-42",
    value=b'{"event":"placed","amount":1999}',
    on_delivery=on_delivery,
)
p.flush()                          # block until queued messages are delivered
No key = round-robin = no ordering

Jika Anda tidak set key, record tersebar lintas partisi (round-robin / sticky), memaksimalkan paralelisme tapi melepas hubungan urutan apa pun di antara mereka. Itu baik untuk event independen; itu bug jika event itu perlu tetap terurut (semua pembaruan satu akun, misalnya). Key berdasarkan batas urutan/entitas Anda: biasanya ID entitas, kapan pun urutan penting (bagian 10).

07

Consumer & Consumer Group

Consumer membaca record dari partisi. Abstraksi yang kuat consumer group: satu set consumer berbagi group ID yang secara kooperatif membagi partisi topic di antara mereka supaya setiap partisi dibaca tepat satu consumer dalam group. Tambah consumer ke group dan Kafka rebalance, menetapkan ulang partisi untuk menyebar beban. Begitulah Anda scaling konsumsi horizontal, dan ini mekanisme di balik berbagi-kerja dan broadcast.

Consumer groups
Within a group, partitions are split; different groups each get everything
topic: 4 partitions P0 P1 P2 P3 group A (2 consumers) c1 <- P0,P1 c2 <- P2,P3 group B (1 consumer) c1 <- P0,P1,P2,P3 group A splits the load (scale out within a group) group B is independent it sees ALL events too Same group = work-sharing (each event once). Different groups = broadcast (each group gets everything). max useful consumers in a group = number of partitions (extra consumers sit idle)
c, _ := kafka.NewConsumer(&kafka.ConfigMap{
    "bootstrap.servers": "localhost:9092",
    "group.id":          "billing",        // the consumer GROUP, scale by adding instances
    "auto.offset.reset": "earliest",       // where to start if no committed offset (sec 8)
    "enable.auto.commit": false,           // we'll commit manually for at-least-once (sec 9)
})
defer c.Close()
c.Subscribe("orders", nil)                 // Kafka assigns this instance some partitions

for {
    msg, err := c.ReadMessage(-1)          // blocks for the next record
    if err != nil { continue }

    process(msg.Value)                     // do the work FIRST...
    c.CommitMessage(msg)                   // ...THEN commit the offset (at-least-once, sec 9)
}
from confluent_kafka import Consumer

c = Consumer({
    "bootstrap.servers": "localhost:9092",
    "group.id": "billing",                 # the consumer GROUP, scale by adding instances
    "auto.offset.reset": "earliest",       # where to start if no committed offset (sec 8)
    "enable.auto.commit": False,           # commit manually for at-least-once (sec 9)
})
c.subscribe(["orders"])                    # Kafka assigns this instance some partitions

try:
    while True:
        msg = c.poll(1.0)                  # wait up to 1s for the next record
        if msg is None:
            continue
        if msg.error():
            print("error:", msg.error()); continue

        process(msg.value())               # do the work FIRST...
        c.commit(msg)                      # ...THEN commit the offset (at-least-once, sec 9)
finally:
    c.close()                              # leave the group cleanly -> triggers a rebalance
Partitions cap your parallelism

Karena satu partisi dikonsumsi paling banyak satu consumer per group, sebuah group bisa berguna memiliki paling banyak consumer aktif sebanyak partisi topic. Topic 4 partisi dengan 6 anggota group meninggalkan 2 consumer menganggur. Jadi paralelisme konsumsi maksimum Anda ditetapkan jumlah partisi (bagian 4), alasan lain mengukur partisi untuk throughput puncak Anda di depan (bagian 20).

Part III / Guarantees
08

Offset & Manajemen Offset

Offset posisi record di partisinya (bagian 3). Tugas consumer melacak offset mana yang sudah ia proses, committed offset-nya: supaya saat restart atau rebalance ia melanjutkan dari tempat yang tepat alih-alih membaca ulang semuanya atau melewatkan record. Kafka menyimpan offset committed ini di topic internal (_consumeroffsets), ber-key group + partisi. Celah antara offset terbaru di partisi dan committed offset group adalah consumer lag: metrik kesehatan terpenting tunggal untuk consumer (bagian 19).

Position & lag
Committed offset, current position, and the lag to the tail
processed & committed read, not yet committed unread (lag) committed offset current position log end (tail) LAG = tail - position Growing lag means the consumer is falling behind

Kapan Anda commit menentukan jaminan Anda

Pilihan antara auto-commit (Kafka secara berkala commit di latar) dan manual commit (Anda commit eksplisit) sebenarnya pilihan tentang semantik pengiriman (bagian 9). Urutan "proses pesan" versus "commit offset" segalanya: commit setelah memproses dan crash di antara berarti Anda akan memproses ulang (at-least-once); commit sebelum memproses dan crash berarti Anda akan melewatkan (at-most-once). Auto-commit pada timer bisa melakukan entah yang mana tergantung timing, itulah kenapa consumer yang sensitif kebenaran commit manual setelah kerja selesai (seperti kode bagian 7).

Offsets are per group, and you can seek

Karena setiap group punya offset committed sendiri, group baru bisa mulai dari earliest dan memutar ulang seluruh riwayat yang dipertahankan, dasar untuk mem-bootstrap consumer baru atau membangun ulang tampilan (bagian 14). Anda juga bisa eksplisit seek consumer ke offset atau timestamp mana pun untuk memproses ulang rentang. Immutabilitas log yang dipertahankan (bagian 3) yang membuat ini mungkin; antrean tidak bisa melakukannya.

09

Semantik Pengiriman

Pertanyaan yang harus dijawab setiap sistem pesan terdistribusi: jika hal gagal dan retry, berapa kali sebuah pesan mungkin dikirim? Ada tiga jaminan mungkin, dan Kafka bisa memberi Anda salah satunya tergantung konfigurasi, jadi Anda harus memilih secara sadar berdasarkan apa yang bisa ditoleransi pemrosesan Anda.

Three semantics
The trade-off between losing messages and duplicating them
At-most-once commit BEFORE processing may LOSE messages never duplicates ok for lossy metrics At-least-once commit AFTER processing never loses may DUPLICATE the common default + idempotency Exactly-once idempotent producer + txns no loss, no dupes costlier / Kafka-to-Kafka read-process-write pipelines Most systems choose at-least-once and make processing idempotent, the pragmatic sweet spot.
At-least-once + idempotent consumers = the practical answer

Exactly-once end-to-end sejati sempit: enable.idempotence Kafka menghentikan duplikat producer, dan transaksi memberi exactly-once untuk stream processing Kafka-ke-Kafka (konsumsi, transformasi, produksi, dan commit offset secara atomik). Tapi begitu consumer Anda menulis ke sistem eksternal (database, API pembayaran), exactly-once melintasi batas itu tidak gratis. Pendekatan teruji perang: pilih at-least-once dan buat pemrosesan Anda idempotent: aman menerapkan event yang sama dua kali (dedup dengan event ID, upsert alih-alih insert). Ini disiplin idempotensi yang sama dengan bab task-queues (bagian 14 di sana) dan idempotency key REST, dan itulah kenapa duplikat jadi tidak berbahaya alih-alih katastrofal.

making at-least-once safe: idempotent processing (pseudocode, both languages)
-- The consumer may see the same event twice on retry/rebalance.
-- Make the write idempotent so a duplicate is a no-op:

-- (a) dedupe by a unique event id
INSERT INTO processed_events (event_id) VALUES ($1)
ON CONFLICT (event_id) DO NOTHING;        -- second delivery inserts nothing
-- ...only do the side effect if the insert affected a row.

-- (b) or make the effect itself idempotent, upsert the end state, don't increment
INSERT INTO account_balance (account_id, balance) VALUES ($1, $2)
ON CONFLICT (account_id) DO UPDATE SET balance = EXCLUDED.balance;
-- applying the same "balance = 100" twice lands on the same state.
10

Jaminan Urutan

Satu kalimat untuk dihafal: Kafka menjamin urutan dalam partisi, dan tidak di tempat lain. Record di partisi P dikirim ke consumer dalam urutan offset persis saat ditulis. Lintas partisi topic yang sama, tidak ada urutan global, record saling bersisip arbitrer. Ini bukan keterbatasan yang harus dikerjakan melainkan harga langsung paralelisme tingkat partisi (bagian 4): Anda bisa punya urutan total, atau skala horizontal, tapi tidak keduanya sekaligus untuk stream yang sama.

Ordering vs parallelism
Keying preserves per-entity order; the trade-off is partition skew
key = order id -> same order's events stay together & ordered producer P0 o42:placedo42:paido42:shipped P1 o88:placedo88:paid o42 strictly ordered in P0; o88 strictly ordered in P1; P0 vs P1 order = undefined Risk: a "hot" key (one huge customer) overloads its single partition, partition skew (sec 18). Choose the key at the granularity where order matters but load still spreads.
Two ordering foot-guns

Pertama, key skew: jika urutan menuntut keying pada nilai kasar (mis. satu mega-tenant), semua lalu lintas tenant itu mengerucut ke satu partisi, membatasi throughput-nya berapa pun jumlah partisi yang ada (bagian 18). Kedua, retry bisa mengubah urutan: dengan producer non-idempotent dan banyak request in-flight, pesan yang di-retry bisa mendarat setelah yang belakangan. Mengaktifkan enable.idempotence (bagian 6) mengawetkan urutan per-partisi bahkan lintas retry, alasan lain ia default yang masuk akal.

11

Retensi & Kompaksi Log

Karena record dipertahankan setelah dibaca (bagian 2), Kafka butuh aturan kapan menghapusnya, kalau tidak log tumbuh selamanya. Ada dua kebijakan pembersihan, dan mereka menjawab kebutuhan berbeda. Retensi waktu/ukuran (default) menyimpan record selama jendela, katakanlah 7 hari atau 50 GB per partisi, lalu menghapus yang tertua. Kompaksi log lebih cerdas: ia menyimpan record terbaru untuk tiap key dan mengumpulkan versi lama key itu, mengubah log menjadi snapshot yang terus diperbarui dari "nilai saat ini per key."

Retention vs compaction
Delete by age, or keep the latest value per key
delete policy (time/size), drop the oldest oldoldkeepkeepkeep records past the window are deleted regardless of key compact policy, keep latest per KEY before: k=A v=1k=B v=1k=A v=2k=A v=3 after: k=B v=1k=A v=3 only the newest value of each key survives, a live snapshot Compaction makes a topic usable as a durable, replayable key/value store of current state.
Compaction powers state & the replay story

Topic ter-kompaksi cara Kafka memegang state saat ini selamanya tanpa pertumbuhan tak terbatas, basis untuk snapshot event-sourcing (bagian 14), state store dan KTable Kafka Streams (bagian 16), dan data config/pencarian yang bisa diputar ulang consumer baru dari nol untuk membangun ulang gambaran lengkap. Topic retensi-waktu menjawab "apa yang terjadi baru-baru ini"; topic ter-kompaksi menjawab "apa state terbaru dari semuanya." Pilih kebijakan berdasarkan pertanyaan mana yang ada untuk dijawab topic Anda.

Part IV / Schemas & Patterns
12

Skema & Schema Registry

Kafka sendiri memperlakukan nilai record sebagai byte buram, ia tidak tahu atau peduli isinya. Kebebasan itu jebakan di skala: jika producer dan consumer tidak setuju pada bentuk payload, producer yang mengubah field diam-diam memecah setiap consumer hilir (bahaya contract-drift yang diperingatkan bab gRPC dan serialisasi, kini tersebar lintas banyak tim independen). Perbaikannya skema untuk tiap topic plus Schema Registry yang menyimpan skema, menetapkan ID, dan menegakkan kompatibilitas saat mereka berevolusi.

Schema Registry
Producers and consumers share a registered, versioned schema
producerserialize + schema id Schema Registryversioned / checks compat topic (bytes + id)Kafka stores only bytes consumerfetch schema, decode register produce fetch by id The registry rejects an incompatible schema change, breaking changes fail at deploy, not in production.

Format & kompatibilitas

Format serialisasi umum Avro (pasangan tradisional Kafka, ringkas dengan kisah evolusi skema kaya), Protobuf (IDL yang sama dengan bab gRPC, makin populer), dan JSON Schema (terbaca, lebih besar). Registry menegakkan mode kompatibilitas: biasanya kompatibilitas backward, artinya consumer baru bisa membaca data lama: Anda boleh menambah field opsional dan menghapus yang punya default, tapi tidak boleh mengganti nama atau mengubah tipe field. Ini persis disiplin perubahan aditif dari bab gRPC (bagian 04 di sana: nomor field, jangan pernah pakai ulang) dan versioning REST, ditegakkan otomatis oleh server.

A schema is a cross-team contract

Di armada microservice, skema topic API publik yang diandalkan banyak tim independen. Schema Registry mengubah "tolong jangan pecahkan payload" dari konvensi suku menjadi gerbang yang diperiksa mesin, peran yang sama dimainkan buf untuk file .proto gRPC. Perlakukan perubahan skema dengan perhatian yang sama seperti perubahan API apa pun: aditif dan kompatibel secara default.

13

Pola Event-Driven

Kafka substrat untuk event-driven architecture (EDA), tempat layanan berkomunikasi dengan memancarkan dan bereaksi terhadap event alih-alih saling memanggil langsung. Alih-alih service order memerintah service email, inventaris, dan analitik (kopling ketat, service order harus kenal semuanya), ia cukup mengumumkan "OrderPlaced" ke topic, dan service mana pun yang tertarik berlangganan. Producer tidak tahu, atau peduli, siapa yang mengonsumsi. Ini pembayaran dalam dari decoupling log (bagian 1): Anda menambah consumer baru tanpa menyentuh producer.

Choreography
One event, many independent reactions, the producer is unaware
order serviceemits OrderPlaced topic: orders email service inventory service analytics service Add a "loyalty-points" consumer tomorrow, zero changes to the order service.
PolaApa yang dibawa eventKompromi
Event notificationHanya "X terjadi" + IDPesan kecil; consumer harus memanggil balik untuk detail (kopling, beban)
Event-carried state transferState relevan penuh di eventConsumer mandiri (tanpa callback); pesan lebih besar, kemungkinan duplikasi
ChoreographyService bereaksi ke event secara otonomKopling longgar, mudah diperluas; alur lebih sulit dilihat end-to-end
OrchestrationKoordinator menggerakkan langkah-langkah (saga)Alur eksplisit terlihat; memperkenalkan kembali koordinator sentral
Decoupling isn't free

EDA menukar kejelasan panggilan langsung demi fleksibilitas. Sisi bawahnya tidak ada satu tempat yang mendeskripsikan seluruh alur kerja, "apa yang terjadi saat order ditempatkan?" kini tersebar di banyak subscriber, yang membuat debugging dan penalaran lebih sulit. Mitigasi dengan observabilitas baik (trace/correlation ID lewat header event, gagasan yang sama dengan bagian 13/bagian 15 bab lapisan dan logging), skema event jelas (bagian 12), dan memilih orkestrasi (saga eksplisit) saat alur benar-benar butuh urutan terkoordinasi yang terlihat.

14

Event Sourcing & CQRS

Dua pola lanjutan yang dimungkinkan log, sering dikacaukan, sering dipasangkan. Event sourcing menyimpan state sebagai urutan event yang menghasilkannya alih-alih hanya snapshot saat ini: alih-alih baris balance = 100, Anda menyimpan Deposited 60, Deposited 50, Withdrew 10, dan menurunkan saldo dengan memutar ulangnya. Log immutable terurut yang dipertahankan Kafka (bagian 3) event store alami. CQRS (Command Query Responsibility Segregation) memisahkan model tulis dari satu atau lebih model baca yang dioptimalkan untuk query.

Event sourcing + CQRS
The event log is the source of truth; read models are derived projections
command"deposit 60" event logSOURCE OF TRUTH (append-only) read model: search indexprojection for fast queries read model: SQL viewprojection for reporting Lost a read model? Rebuild it by replaying the log from offset 0. The events are the truth; views are disposable.

Manfaatnya nyata: jejak audit lengkap (setiap perubahan fakta tercatat, emas untuk finansial dan kepatuhan), kemampuan membangun ulang state di titik masa lalu mana pun dengan memutar ulang ke offset, dan banyak model baca khusus yang masing-masing bisa dibangun ulang dari log yang sama (kompaksi, bagian 11, menyediakan snapshot untuk menghindari memutar ulang dari awal zaman).

Powerful, and not free, don't reach for it by default

Event sourcing menambah kompleksitas nyata: evolusi skema event historis sulit (Anda tidak pernah bisa mengubah masa lalu, hanya cara menafsirkannya), membangun ulang state punya biaya, dan "apa nilai saat ini?" kini butuh proyeksi alih-alih baca baris. Ia bersinar untuk domain audit-berat dan kompleks (pembayaran, buku besar, tepat fintech) tempat riwayat adalah nilainya. Untuk CRUD biasa, database normal lebih sederhana dan benar, pakai polanya tempat jejak audit dan replay benar-benar membayar kompleksitasnya, bukan sebagai arsitektur default.

15

Pola Outbox & Change Data Capture

Masalah halus dan penting di sistem event-driven: saat service harus memperbarui database-nya dan menerbitkan event, melakukannya sebagai dua operasi terpisah tidak aman. Jika commit DB sukses tapi publish Kafka gagal (atau sebaliknya), sistem Anda menyimpang, order ada tapi tidak ada event "OrderPlaced", atau event menyala untuk order yang di-rollback. Ini masalah dual-write, dan Anda tidak bisa memperbaikinya dengan menukar urutan dua tulis, selalu ada jendela crash di antara keduanya.

The dual-write problem & the outbox fix
Write the event into the same DB transaction; relay it to Kafka afterward
naive dual write, can diverge commit DB → ⚡ crash here publish Kafka DB updated but event never sent outbox pattern, atomic ONE DB transaction orders row outbox row relay / CDC Kafka topic Both rows commit together or not at all; a separate relay reliably forwards the outbox, no lost or phantom events.

Pola outbox menyelesaikannya dengan satu transaksi lokal: tulis perubahan bisnis Anda dan baris "outbox" yang mendeskripsikan event ke dalam transaksi database yang sama, jadi mereka commit secara atomik. Proses relay terpisah lalu membaca baris outbox baru dan menerbitkannya ke Kafka (retry sampai sukses, at-least-once, karenanya consumer tetap idempotent, bagian 9). Cara modern ber-friction-rendah menjalankan relay itu Change Data Capture (CDC): tool seperti Debezium men-tail log transaksi database dan meng-stream perubahan yang di-commit langsung ke Kafka, tanpa polling aplikasi.

outbox: business write + event in one transaction
BEGIN;

-- 1) the actual business change
INSERT INTO orders (id, customer_id, amount, status)
VALUES ('order-42', 'cust-7', 1999, 'PLACED');

-- 2) the event to publish, written in the SAME transaction -> atomic with (1)
INSERT INTO outbox (id, aggregate, event_type, payload, created_at)
VALUES (gen_random_uuid(), 'order-42', 'OrderPlaced',
        '{"id":"order-42","amount":1999}', now());

COMMIT;   -- both succeed together, or neither does, no dual-write gap

-- A relay (or Debezium CDC on the transaction log) then reads new `outbox`
-- rows and produces them to Kafka, marking each published. Retries are safe
-- because downstream consumers are idempotent (sec 9).
The standard answer to "DB + Kafka together"

Kapan pun sebuah tulis harus secara atomik menghasilkan event, umum di pembayaran dan sistem order, raih outbox (sering via Debezium CDC). Ia mengubah masalah transaksi terdistribusi yang mustahil menjadi transaksi lokal biasa plus relay idempotent yang andal. Ini salah satu pola paling berharga praktis di seluruh toolkit event-driven.

Part V / Processing, Ops & Practice
16

Stream Processing

Sejauh ini consumer membaca event satu per satu. Stream processing disiplin menghitung secara berkelanjutan di atas stream, memfilter, mentransformasi, mengagregasi, meng-join, menghasilkan stream turunan baru. Alih-alih "baca event, lakukan satu hal," Anda mengekspresikan query berdiri seperti "hitungan bergulir 5 menit dari pembayaran gagal per merchant" yang diperbarui selamanya saat event tiba. Library Kafka untuk ini Kafka Streams (JVM); ksqlDB menawarkan lapisan mirip SQL, dan Apache Flink kelas berat untuk job besar kompleks.

KonsepMakna
KStreamStream tak terbatas dari event independen (tampilan append log), setiap record penting.
KTableChangelog yang ditafsirkan sebagai state saat ini per key, tampilan topic ter-kompaksi (bagian 11); nilai terbaru menang.
Operasi ber-stateAgregasi/join yang mengingat lintas event, didukung state store lokal (sendiri topic Kafka ter-kompaksi untuk toleransi kegagalan).
WindowingMengelompokkan event berdasarkan waktu (tumbling, hopping, session) untuk mengagregasi "per 5 menit," dll.
Event vs processing timeKapan event terjadi vs kapan Anda memproses-nya, keduanya berbeda, dan data terlambat/tak-terurut harus ditangani.
The stream/table duality

Gagasan inti yang elegan: stream dan table dua tampilan dari data yang sama. Stream perubahan yang diagregasi per key memberi table (state saat ini); changelog table adalah stream pembaruan. Ini persis perbedaan retensi-vs-kompaksi (bagian 11) dinaikkan ke model pemrograman, dan itulah kenapa topic ter-kompaksi adalah table dan topic normal adalah stream. Kebanyakan engineer backend mengonsumsi stream dengan consumer polos (bagian 7); raih framework stream-processing saat Anda butuh agregasi ber-state, join, atau windowing yang menyakitkan jika diracik tangan.

17

Kafka vs Antrean Tradisional

Kembali ke perbedaan bagian 2 dengan konteks cukup untuk memutuskan dengan baik. Ini pertanyaan yang mengirim orang ke bab task-queues dan kembali: apakah saya mau Kafka atau message queue (RabbitMQ, SQS, dunia Celery/Sidekiq)? Mereka cukup tumpang tindih untuk membingungkan dan cukup berbeda untuk berpengaruh.

DimensiKafka (log)RabbitMQ / SQS (antrean)
Model intiLog event tahan lama, bisa diputar ulangAntrean kerja sementara
Setelah konsumsiDipertahankan; bisa diputar ulang group mana punDihapus setelah di-ack
Fan-out ke banyak sistemNatif, tiap group membaca semuaButuh konfig exchange/fan-out; bukan default
ThroughputSangat tinggi (jutaan/detik)Tinggi, tapi biasanya lebih rendah
UrutanKuat per partisiLemah; mode FIFO terbatas/lambat
Routing/prioritas/TTL per pesanTerbatas, bukan modelnyaKaya (routing key, prioritas, delay, DLQ)
Replay / time-travelYa, seek ke offset mana punTidak
Terbaik untukEvent streaming, banyak subscriber, replay, analitik, event sourcingOffloading tugas, job mirip RPC, routing kompleks, kontrol per-pesan
The decision heuristic

Rengkuh Kafka saat event adalah fakta yang dipedulikan banyak sistem, saat Anda butuh throughput tinggi, urutan, atau kemampuan memutar ulang riwayat, tulang punggung streaming. Rengkuh antrean (bab 10) saat Anda menyerahkan unit kerja diskret untuk dilakukan sekali, terutama dengan routing per-pesan, prioritas, delay, atau penanganan dead-letter. Mereka saling melengkapi: sangat banyak sistem nyata menjalankan Kafka sebagai tulang punggung event dan task queue untuk job latar. Jangan memaksa semantik task-queue ke Kafka (TTL per-pesan, prioritas, ack selektif), itu berenang melawan desainnya.

18

Jebakan Consumer

Kebanyakan rasa sakit Kafka produksi muncul di sisi consumer. Ini mode kegagalan yang menggigit tim nyata, layak dikenali sebelum mereka page Anda jam 2 pagi.

JebakanApa yang terjadiMitigasi
Badai rebalanceRebalance group yang sering (pemrosesan lambat memicu poll timeout, anggota dinyatakan mati) menghentikan konsumsi berulangJaga kerja per-poll terbatas, setel max.poll.records / max.poll.interval.ms; geser kerja lambat; pakai cooperative/incremental rebalancing
Poison pillSatu pesan yang tidak bisa diproses (skema buruk, data buruk) membuat consumer crash & retry selamanya, partisi macetTangkap error per-record; rutekan record buruk ke dead-letter topic dan lanjutkan
Consumer lagProducer melampaui consumer; lag tumbuh tanpa batas; data basiTambah partisi + consumer (bagian 7), percepat pemrosesan, batch; alert pada lag (bagian 19)
Skew partisi / keyKey panas (satu tenant raksasa) membebani partisi tunggalnya sementara yang lain menganggur (bagian 10)Pilih key kardinalitas lebih tinggi, atau key komposit; isolasi tenant panas
Pemrosesan lambat memblokir urutanUntuk menjaga urutan Anda memproses partisi serial, jadi satu record lambat menunda sisanyaParalelkan lintas partisi, bukan di dalam; pertimbangkan pola paralelisme per-key
Reprocessing pada celah commitCrash antara pemrosesan dan commit offset mengirim ulang recordDiharapkan di bawah at-least-once, buat pemrosesan idempotent (bagian 9)
Always have a dead-letter strategy

Satu pesan salah bentuk tidak boleh pernah mampu menghentikan seluruh partisi tanpa batas, itu poison pill, dan penyebab pemadaman umum. Bungkus pemrosesan per-record dalam penanganan error: pada kegagalan persisten, terbitkan record yang bermasalah (plus error dan metadata) ke dead-letter topic, commit melewatinya, dan jaga stream mengalir. Anda menyelidiki dead-letter topic belakangan alih-alih membiarkan satu record buruk memblokir semua orang, disiplin DLQ yang sama yang diterapkan bab task-queues pada job gagal.

19

Operasi & Monitoring

Menjalankan Kafka dengan baik bermuara pada memantau beberapa sinyal dan mengukur beberapa hal dengan benar. Anda tidak perlu menghafal setiap metrik, tapi harus tahu yang memprediksi masalah.

PantauKenapa penting
Consumer lagMetrik kesehatan utama (bagian 8): lag yang tumbuh berarti consumer tidak bisa mengejar, data makin basi. Alert padanya.
Partisi under-replicatedReplika jatuh keluar dari ISR (bagian 5), daya tahan berisiko; broker mungkin kesulitan.
Disk & throughput brokerRetensi (bagian 11) berarti topic mengonsumsi disk nyata; disk atau jaringan jenuh men-throttle klaster.
Frekuensi rebalanceRebalance sering (bagian 18) menandakan consumer tidak stabil dan menyebabkan keheningan.
Latensi & error requestLatensi dan tingkat error produce/fetch memunculkan masalah broker atau klien lebih awal.

Keputusan sizing yang benar-benar Anda buat

  • Jumlah partisi: men-set paralelisme consumer maksimum dan throughput (bagian 4, bagian 7). Perkirakan dari target throughput dan jumlah consumer puncak; condong sedikit tinggi, tapi tidak liar (tiap partisi punya overhead, dan ribuan per broker menyakitkan). Sulit diubah belakangan, jadi pikirkan ke depan (bagian 20).
  • Faktor replikasi: biasanya 3 di produksi: mentoleransi satu kehilangan broker dengan ruang cadangan. Pasangkan dengan min.insync.replicas=2 dan acks=all (bagian 5).
  • Retensi: berapa lama event hidup (dan karenanya pemakaian disk dan jendela replay). Set per topic seberapa jauh ke belakang consumer mungkin perlu membaca.
Tooling

Observabilitas standar: ekspor metrik JMX broker dan klien ke Prometheus + Grafana (stack yang sama dipakai bab task-queues dan logging), dan awasi consumer lag secara khusus dengan lag exporter atau UI seperti Kafka UI / Conduktor / AKHQ. kafka-consumer-groups.sh --describe memberi bacaan CLI cepat lag group per partisi saat men-debug langsung.

20

Mendesain dengan Kafka

Menarik manual ke penilaian desain. Kafka tool kuat dan tajam; dipakai di tempatnya transformatif, dipakai sebagai message bus generik liabilitas. Beberapa prinsip memisahkan desain Kafka yang baik dari yang menyakitkan.

Prinsip desain

  • Modelkan topic di sekitar event/fakta, bukan perintah atau RPC. Topic adalah stream dari "hal yang terjadi" (PaymentSettled), dikonsumsi siapa pun yang peduli, bukan cara menyuruh service spesifik melakukan sesuatu. Jika Anda mendapati ingin request/response, Anda ingin gRPC/REST, bukan Kafka.
  • Pilih partition key berdasarkan batas urutan Anda. Key berdasarkan entitas yang event-nya harus tetap terurut (akun, order, user), tapi pada granularitas yang tetap menyebar beban, untuk menghindari partisi panas (bagian 10, bagian 18). Key keputusan per-topic paling konsekuensial.
  • Asumsikan at-least-once; bangun consumer idempotent. Jangan mengejar exactly-once end-to-end lintas sistem eksternal (bagian 9). Dedup dengan event ID atau pakai upsert supaya duplikat tidak berbahaya, lebih murah dan lebih kokoh.
  • Kelola skema sejak hari pertama. Pakai Schema Registry dengan kompatibilitas backward (bagian 12) supaya tim independen bisa berevolusi dengan aman. Skema topic kontrak.
  • Pakai outbox untuk tulis DB-dan-event. Jangan pernah dual-write; buat event atomik dengan data dan relay (bagian 15).
  • Ukur partisi untuk puncak di depan. Repartisi memecah urutan key (bagian 4), jadi rencanakan pertumbuhan alih-alih bereaksi padanya.
When Kafka, and when not

Rengkuh Kafka saat Anda punya stream event yang dikonsumsi banyak sistem, butuh throughput tinggi atau replay, sedang melakukan event-driven architecture, event sourcing, pipeline analitik, atau ingestion log/metrik. Jangan merengkuhnya untuk request/response sederhana (pakai gRPC/REST, bab gRPC), untuk job latar diskret dengan routing atau prioritas per-pesan (pakai task queue, bab 10), atau sebagai satu-satunya datastore untuk pencarian titik (pakai database). Mencocokkan tool ke bentuk masalah seluruh keterampilannya.

21

Crib-Sheet

Seluruh manual dipadatkan menjadi yang Anda raih saat tertekan.

KonsepSatu baris
KafkaCommit log append-only terdistribusi, tahan lama, dari event yang dibaca banyak consumer di offset mereka sendiri.
Antrean vs logAntrean: dikonsumsi sekali & dihapus. Log: dipertahankan, bisa diputar ulang, tiap group membaca semua.
Commit logAppend-only, immutable, ber-alamat offset, I/O sekuensial, basis dari segalanya.
TopicStream bernama, dipecah menjadi partisi.
PartisiUnit paralelisme, urutan, dan penyimpanan. Key sama -> partisi sama.
Broker / replikasiKlaster server; tiap partisi punya replika leader + follower (ISR).
acks / ISRacks=all + min.insync.replicas=2 = tanpa kehilangan data pada satu kegagalan broker.
ProducerMenambahkan record ber-key; key menentukan partisi; idempotence menghindari dup saat retry.
Consumer groupMembagi partisi di antara anggota. Group sama = berbagi kerja; group berbeda = broadcast.
Batas paralelismeConsumer berguna maksimum per group = jumlah partisi.
Offset / lagPosisi Anda per partisi; lag = ekor - posisi = metrik kesehatan kunci.
Waktu commitCommit setelah memproses = at-least-once; sebelum = at-most-once.
PengirimanAt-most / at-least / exactly-once. Pilih at-least-once + consumer idempotent.
UrutanDijamin hanya dalam partisi. Key berdasarkan batas urutan Anda.
RetensiHapus berdasarkan waktu/ukuran, atau kompak untuk menyimpan nilai terbaru per key.
KompaksiTerbaru-per-key -> snapshot state saat ini yang tahan lama dan bisa diputar ulang (table).
Schema RegistrySkema ber-versi (Avro/Protobuf) dengan kompatibilitas backward yang ditegakkan.
EDAPancarkan event; consumer bereaksi independen, kopling longgar, mudah diperluas.
Event sourcing / CQRSLog sumber kebenaran; model baca proyeksi yang bisa dibangun ulang.
Outbox / CDCTulis data + event dalam satu txn DB; relay (Debezium) ke Kafka, tanpa celah dual-write.
Stream processingKomputasi berkelanjutan (filter/agregasi/join/window); dualitas KStream vs KTable.
JebakanBadai rebalance, poison pill (pakai DLQ), lag, key skew.
vs antreanKafka = event streaming/replay/throughput; antrean = kerja + routing/prioritas. Pakai keduanya.

Seluruh topik dalam satu tarikan napas: Kafka commit log append-only terdistribusi, tahan lama (bagian 1-3), tidak seperti antrean, event dipertahankan dan setiap consumer group membaca semuanya dan bisa memutar ulang (bagian 2). Topic dipecah menjadi partisi, unit paralelisme, urutan, dan penyimpanan (bagian 4), tersebar lintas broker dan direplikasi untuk daya tahan via leader, follower, dan ISR (bagian 5). Producer menambahkan record ber-key (bagian 6); consumer group membagi partisi untuk scaling keluar, dengan paralelisme dibatasi jumlah partisi (bagian 7). Setiap consumer melacak offsetnya, dan lag ke ekor sinyal kesehatan kunci (bagian 8); kapan Anda commit menentukan semantik pengiriman Anda, dan pilihan pragmatis at-least-once dengan consumer idempotent (bagian 9). Urutan hanya berlaku dalam partisi, jadi Anda key berdasarkan batas urutan dan waspadai skew (bagian 10); retensi menghapus berdasarkan usia sementara kompaksi menyimpan nilai terbaru per key (bagian 11). Schema Registry mengatur payload lintas tim (bagian 12); log menopang event-driven architecture (bagian 13), event sourcing dan CQRS (bagian 14), dan pola outbox/CDC untuk tulis DB-dan-event atomik (bagian 15). Stream processing menghitung berkelanjutan di atasnya (bagian 16); pilih Kafka di atas antrean untuk streaming, replay, dan throughput (bagian 17), jaga dari badai rebalance, poison pill, lag, dan skew (bagian 18), pantau lag dan ISR (bagian 19), dan rancang topic di sekitar event yang dipilih key dengan masuk akal, dengan consumer idempotent dan skema terkelola (bagian 20).

Berpijak pada dokumentasi Apache Kafka & Confluent / literatur log/stream-table (Kreps) / Debezium untuk CDC / contoh Go 1.22+ (confluent-kafka-go) / Python 3.11+ (confluent-kafka).