Konsep streaming

Konsep dasar streaming, yaitu kejadian, topik, dan offset.

Daftar isi

Topik ini memperkenalkan konsep dasar pemrosesan streaming, yaitu pemrosesan data kejadian demi kejadian segera setelah datanya tiba. Empat istilah intinya diuraikan satu per satu. Kejadian (event) menjadi unit data, topik (topic) menjadi log tambah-saja (append-only), partisi (partition) menjadi unit paralelisme, dan offset menjadi penanda posisi baca. Satu simulasi Python kemudian dijalankan untuk menunjukkan perilaku log dan offset secara nyata.

Kejadian sebagai unit data streaming#

Streaming memproses data sebagai aliran kejadian, yaitu catatan atas satu peristiwa yang terjadi pada satu titik waktu, seperti sensor suhu yang melaporkan nilai 24,1 pada pukul 08.02.00. Dalam terminologi Kafka, satu unit data disebut record, dan sepanjang jalur ini istilah kejadian dipakai untuk menyebutnya. Satu kejadian umumnya membawa tiga bagian, yaitu kunci (key) yang menentukan partisinya, nilai (value) yang berisi datanya, dan stempel waktu (timestamp) yang menyatakan kapan peristiwanya terjadi.

Kejadian berbeda dari baris tabel karena sifatnya sebagai fakta yang sudah terjadi. Baris tabel boleh diperbarui, sedangkan kejadian yang sudah tertulis tidak pernah diubah. Nilai terkini, misalnya suhu terakhir sebuah ruangan, diturunkan dari rangkaian kejadian itu, bukan disimpan langsung sebagai satu nilai tunggal. Cara pandang ini penting dipahami lebih dahulu, karena seluruh arsitektur Kafka dibangun untuk menyimpan dan menyalurkan fakta, bukan untuk memperbarui keadaan.

Topik sebagai log tambah-saja#

Topik adalah nama tempat kejadian yang sejenis dikumpulkan, dan setiap topik menyimpan datanya dalam bentuk log tambah-saja. Log tambah-saja berarti kejadian baru hanya ditambahkan di ujung log, sedangkan kejadian yang sudah masuk tidak pernah diubah maupun dihapus. Pola ini membuat topik berperilaku seperti log aplikasi yang terus bertambah, bukan seperti tabel yang isinya selalu diperbarui.

Penyimpanan pada log tidak berlangsung selamanya karena setiap topik punya retensi (retention), yaitu batas waktu atau batas ukuran tempat kejadian disimpan sebelum dibuang. Kejadian yang masih dalam masa retensi dapat dibaca ulang dari awal maupun dari titik mana pun, dan kemampuan inilah yang membedakan log dari antrean pesan konvensional. Antrean menghilangkan pesannya setelah dibaca, sedangkan log mempertahankannya agar consumer, yaitu program pembaca kejadian, tetap dapat membacanya dengan posisi baca sendiri.

Partisi dan offset#

Satu topik dipecah menjadi beberapa partisi, yaitu log tambah-saja yang berdiri sendiri dan sekaligus menjadi unit paralelisme serta unit urutan. Kejadian dengan kunci yang sama selalu masuk ke partisi yang sama sehingga urutannya terjamin, sedangkan dua kejadian pada partisi berbeda tidak saling menjamin urutannya. Offset adalah nomor urut posisi sebuah kejadian di dalam partisinya, dimulai dari 0 dan bertambah satu untuk setiap kejadian baru.

Diagram berikut menggambarkan satu log partisi dengan offset 0 sampai 7. Kejadian baru ditambahkan oleh producer, yaitu program penulisnya, di ujung log, sedangkan panah hijau menunjuk posisi baca consumer berikutnya.

Log partisi sensor-ruang-101 (tambah-saja)012345678kejadian baru masukangka di dalam kotak = offset kejadianposisi consumer: offset 4kejadian 0-3 sudah dibaca
Log partisi dengan offset 0 sampai 7 dan posisi baca consumer

Offset membuat posisi baca menjadi murah untuk disimpan, karena consumer tidak perlu mencatat kejadian mana yang sudah diproses, melainkan cukup menyimpan satu angka per partisi. Angka itu dapat disimpan ulang, dibagikan antar anggota grup, dan dikembalikan saat program dimulai lagi. Baca ulang dari offset yang sama selalu menghasilkan kejadian yang sama, sebab isi lognya tidak pernah berubah.

Simulasi log dan offset#

Perilaku log tambah-saja dan offset dapat dibuktikan dengan Python murni tanpa Kafka sama sekali. Kelas pada skrip berikut meniru satu log partisi, yaitu daftar yang hanya bisa ditambah lewat metode append dan dibaca lewat metode read_from dengan offset awal. Jalankan skripnya, lalu amati urutan offset dan isi bacaan pada tiga situasi yang berbeda.

python simulasi-log.py
"""Simulasi log tambah-saja dengan offset, tanpa Kafka sama sekali."""


class LogPartisi:
    """Log tambah-saja: append menambah di ujung, read_from membaca dari offset."""

    def __init__(self) -> None:
        self.kejadian: list[str] = []

    def append(self, isi: str) -> int:
        """Tambahkan satu kejadian di ujung log, lalu kembalikan offsetnya."""
        self.kejadian.append(isi)
        return len(self.kejadian) - 1

    def read_from(self, offset: int) -> list[tuple[int, str]]:
        """Kembalikan semua kejadian mulai dari offset yang diminta."""
        return list(enumerate(self.kejadian[offset:], start=offset))


log = LogPartisi()
log.append("suhu 24.1")
log.append("suhu 24.3")
log.append("suhu 25.0")

print("baca dari awal (offset 0):")
for offset, isi in log.read_from(0):
    print(f"  offset {offset} : {isi}")

print("baca dari offset 2:")
for offset, isi in log.read_from(2):
    print(f"  offset {offset} : {isi}")

log.append("suhu 24.8")
print("satu kejadian baru masuk, baca dari offset 3:")
for offset, isi in log.read_from(3):
    print(f"  offset {offset} : {isi}")
text Keluaran
baca dari awal (offset 0):
  offset 0 : suhu 24.1
  offset 1 : suhu 24.3
  offset 2 : suhu 25.0
baca dari offset 2:
  offset 2 : suhu 25.0
satu kejadian baru masuk, baca dari offset 3:
  offset 3 : suhu 24.8

Keluarannya menunjukkan tiga perilaku sekaligus. Metode append selalu menambah di ujung dan mengembalikan offset berurutan 0, 1, 2, kemudian 3 untuk kejadian yang masuk belakangan. Metode read_from(2) hanya mengembalikan kejadian mulai offset 2, sehingga pembacaan berulang dari offset yang sama selalu konsisten dengan isi lognya. Bacaan terakhir membuktikan bahwa kejadian baru tidak menggeser offset lama, melainkan hanya menambah posisi baru di ujungnya.

Kesalahan umum#

  1. Menganggap offset bersifat global. Penyebabnya, angka offset sering dibaca seperti nomor urut seluruh topik. Akibatnya, pencarian kejadian menyasar partisi yang salah dan posisi baca tercampur antar partisi. Perbaikannya, simpan offset selalu bersama nama partisinya.
  2. Menulis ulang kejadian lama untuk memperbaiki data. Penyebabnya, kebiasaan memperbarui baris tabel terbawa ke log. Akibatnya, pembaruan tidak mungkin dilakukan karena log memang tidak dapat diubah. Perbaikannya, tulis kejadian koreksi baru di ujung log, lalu biarkan pemrosesan menggabungkan keduanya.
  3. Mengira log menyimpan data selamanya. Penyebabnya, retensi tidak ikut dipikirkan saat topik dibuat. Akibatnya, consumer yang tertinggal kehilangan kejadian yang belum sempat dibacanya karena sudah melewati masa retensi. Perbaikannya, tetapkan masa retensi lebih panjang daripada jeda terlama consumer yang diizinkan.

Ringkasan#

  • Kejadian adalah fakta berstempel waktu yang tidak pernah diubah, dan nilai terkini diturunkan dari rangkaiannya.
  • Topik menyimpan kejadian sebagai log tambah-saja yang terbagi menjadi partisi, dan urutan hanya terjamin di dalam satu partisi.
  • Offset adalah posisi kejadian di dalam partisinya, dan satu angka per partisi sudah cukup untuk menyimpan posisi baca consumer.

Referensi#