Time-Based Retention #
Salah satu perbedaan paling mendasar antara Apache Kafka dengan message broker tradisional (seperti RabbitMQ atau ActiveMQ) terletak pada perilaku penghapusan data. Jika message broker tradisional menghapus pesan segera setelah pesan tersebut berhasil dibaca oleh konsumen (destructive read), Kafka memisahkan proses pembacaan dengan penyimpanan. Kafka menyimpan seluruh event secara permanen ke dalam commit log di disk, memungkinkan data dibaca berkali-kali oleh sistem yang berbeda (non-destructive read). Namun, karena kapasitas penyimpanan disk fisik tidak terbatas, kita membutuhkan kebijakan pembersihan data yang andal. Kebijakan pembersihan default dan yang paling umum digunakan adalah Time-Based Retention (Retensi Berbasis Waktu), yang bertugas memastikan data dibuang secara otomatis setelah melewati batas usia tertentu.
Membedah Tiga Parameter Waktu Retensi #
Kafka menyediakan tiga parameter konfigurasi untuk mengatur durasi penyimpanan log berdasarkan waktu. Ketiganya dapat dikonfigurasi secara global pada server.properties atau ditimpa per topik secara kustom.
1. log.retention.ms (Milidetik)
#
Ini adalah parameter dengan tingkat presisi tertinggi dan sangat direkomendasikan untuk digunakan di lingkungan produksi. Jika kita ingin menerapkan retensi yang sangat ketat (misalnya, menghapus data tepat setelah 12 jam), gunakan parameter ini.
2. log.retention.minutes (Menit)
#
Parameter ini menawarkan tingkat presisi sedang. Jarang digunakan secara langsung kecuali untuk use-case khusus di mana retensi diukur dalam kelipatan menit.
3. log.retention.hours (Jam)
#
Ini adalah parameter bawaan (default) yang paling sering dikonfigurasi secara global. Secara bawaan, Kafka menetapkan:
$$\text{log.retention.hours} = 168 \text{ jam (7 hari)}$$
Artinya, jika kita tidak menentukan konfigurasi apa pun pada topik, pesan akan disimpan selama 7 hari sebelum dihapus.
Aturan Evaluasi Prioritas (Precedence Rule) #
Jika kita mendefinisikan lebih dari satu parameter di atas (misalnya, secara tidak sengaja menyetel log.retention.hours dan log.retention.ms secara bersamaan), Kafka akan memprioritaskan unit waktu yang paling kecil (presisi tertinggi). Urutan evaluasinya adalah:
$$\text{log.retention.ms} \longrightarrow \text{log.retention.minutes} \longrightarrow \text{log.retention.hours}$$
Penyetelan log.retention.ms akan selalu menimpa nilai log.retention.hours meskipun nilai jamnya disetel lebih besar.
Bagaimana Kafka Menentukan Umur Pesan? (Timestamp Dynamics) #
Retensi waktu tidak dapat bekerja tanpa adanya stempel waktu (timestamp) pada setiap record pesan. Kafka menentukan “umur” dari suatu data berdasarkan nilai timestamp yang tertanam di dalam metadata pesan tersebut. Nilai ini dikontrol oleh konfigurasi tingkat topik bernama log.message.timestamp.type.
Ada dua tipe timestamp yang didukung oleh Kafka:
1. CreateTime (Bawaan / Default)
#
Pada mode ini, timestamp diatur oleh produser (klien) saat ia membuat record pesan (new ProducerRecord(...)). Jam lokal sistem produser akan ditulis ke dalam metadata pesan.
Bahaya Laten Clock Drift (Pergeseran Jam Klien) #
Mengandalkan CreateTime di lingkungan produksi memiliki risiko arsitektural yang tinggi jika jam server klien tidak sinkron (clock drift):
- Skenario Masa Depan (Future Timestamp): Jika sebuah produser memiliki jam lokal yang salah disetel ke tahun 2030 (padahal sekarang tahun 2026), pesan yang dikirimnya akan dicatat dengan timestamp tahun 2030. Kafka akan menganggap pesan tersebut baru lahir di tahun 2030, sehingga pesan tersebut tidak akan pernah dihapus oleh retensi waktu selama 4 tahun ke depan, menyumbat ruang disk broker.
- Skenario Masa Lalu (Past Timestamp): Sebaliknya, jika jam klien salah disetel ke tahun 2010, pesan baru tersebut akan langsung dianggap berusia 16 tahun begitu sampai di broker. Sistem retensi waktu Kafka akan langsung mengklasifikasikan pesan tersebut sebagai kedaluwarsa dan menghapusnya seketika pada siklus pemindaian berikutnya. Konsumen tidak akan sempat membaca data tersebut.
2. LogAppendTime (Direkomendasikan untuk Konsistensi Retensi)
#
Ketika kita menetapkan log.message.timestamp.type=LogAppendTime, broker leader akan mengabaikan timestamp apa pun yang dikirim oleh klien. Broker leader akan menimpa timestamp tersebut dengan jam lokal server broker saat ia sukses menulis pesan ke commit log fisiknya.
- Keuntungan: Kebijakan retensi waktu menjadi sangat konsisten, aman, dan prediktif, karena sepenuhnya dikontrol oleh sinkronisasi waktu internal kluster broker (menggunakan protokol NTP - Network Time Protocol).
Membedah File Direktori Partisi: .log, .index, dan .timeindex #
Untuk memahami bagaimana Kafka mengevaluasi retensi waktu secara efisien, kita harus melihat isi fisik dari folder partisi di dalam sistem berkas (filesystem) Linux broker. Setiap partisi (misalnya my-topic-0/) berisi beberapa jenis berkas berikut:
1. File Log (.log)
#
Berkas biner utama yang menampung record pesan aktual secara berurutan. Di sinilah data bisnis kita disimpan.
2. File Indeks Offset (.index)
#
Memetakan offset log ke posisi byte fisik di dalam berkas .log. Membantu mempercepat pencarian data berdasarkan offset.
3. File Indeks Waktu (.timeindex)
#
File ini adalah kunci dari retensi berbasis waktu. Ia memetakan timestamp pesan ke nomor offset terkait.
Ketika Kafka mengevaluasi apakah suatu segmen sudah kedaluwarsa, broker tidak membaca file .log yang sangat besar. Broker cukup membuka file .timeindex yang berukuran kecil dan membaca entry terakhir. Entry terakhir ini mencatat timestamp terbesar (terakhir) dari seluruh pesan di segmen tersebut. Jika timestamp terbesar ini sudah melewati batas retensi waktu, maka seluruh segmen log tersebut dinyatakan kedaluwarsa.
Sinkronisasi Waktu Server Broker Menggunakan NTP #
Karena keandalan retensi waktu dan koordinasi KRaft sangat bergantung pada akurasi waktu antar-server broker, kita wajib mengonfigurasi dan memantau sinkronisasi waktu menggunakan layanan NTP (Network Time Protocol) di seluruh sistem operasi Linux broker.
Jika terjadi pergeseran jam antar-broker (clock drift), misalnya Broker 1 lebih lambat 10 menit dari Broker 2, maka evaluasi kedaluwarsa segmen partisi yang dipindahkan antar-leader dapat mengalami inkonsistensi.
Menggunakan Chrony untuk Sinkronisasi Waktu di Linux #
Di Linux modern, utilitas chrony adalah standar industri untuk sinkronisasi NTP yang cepat dan presisi.
Berikut adalah konfigurasi dasar chrony pada /etc/chrony.conf di broker:
# Menggunakan pool server NTP publik terdekat (misal: Indonesia)
pool id.pool.ntp.org iburst
# Mengizinkan sinkronisasi jam secara bertahap saat startup
initstepslew 10 pool.ntp.org
# Menyimpan data drift jam lokal
driftfile /var/lib/chrony/drift
Untuk memantau status kesehatan sinkronisasi waktu pada sistem operasi broker, kita dapat mengeksekusi perintah berikut:
# Memeriksa apakah waktu server tersinkronisasi dengan baik ke server NTP eksternal
chronyc tracking
# Melihat daftar server sumber waktu yang digunakan dan akurasinya
chronyc sources -v
Cara Kerja Segmentasi File dan Scheduler Pembersih #
Salah satu kunci kecepatan performa I/O Kafka adalah sifat commit log yang immutable (tidak dapat diubah) dan append-only (hanya menulis di akhir). Oleh karena itu, Kafka tidak pernah menghapus pesan secara individual di tengah file. Menghapus pesan satu per satu dari file besar akan memerlukan penataan ulang baris file (shuffling) yang memakan operasi CPU dan I/O disk yang sangat mahal.
Sebagai solusinya, Kafka membagi berkas log fisik partisi menjadi segmen-segmen kecil yang disebut Log Segments.
flowchart TD
subgraph Partisi_Disk ["Folder Partisi Fisik: my-topic-0/"]
direction LR
S1["Segment 1 (Ditutup) <br> Max Timestamp: 8 Hari Lalu <br> Status: Kadaluwarsa"]
S2["Segment 2 (Ditutup) <br> Max Timestamp: 4 Hari Lalu <br> Status: Aman"]
S3["Segment 3 (Aktif) <br> Menampung Penulisan Baru <br> Status: Kebal Retensi"]
end
S1 -->|"Dihapus Utuh dari Disk"| Trash["Tempat Sampah OS"]
style S1 stroke:#c62828,stroke-width:2px
style S2 stroke:#2e7d32,stroke-width:2px
style S3 stroke:#0288d1,stroke-width:2px
1. Konsep Segmen Aktif (Active Segment) #
Segmen tempat broker sedang menulis data baru disebut Active Segment. Hanya ada satu segmen aktif per partisi dalam satu waktu.
- Aturan Kebal: Segmen aktif tidak akan pernah dihapus oleh sistem retensi waktu, meskipun ia berisi pesan-pesan lama yang secara hitungan umur sudah melewati batas retensi. Hal ini dilakukan karena broker masih memerlukan file tersebut untuk menulis data baru.
2. Penutupan Segmen (Segment Rolling) #
Sebuah segmen aktif akan ditutup (rolled) dan berubah status menjadi segmen tidak aktif (inactive segment) ketika salah satu kondisi berikut terpenuhi:
- Ukuran file segmen mencapai batas maksimum (default:
log.segment.bytes = 1 GB). - Durasi waktu penulisan segmen telah terlewati (default:
log.segment.ms = 7 hari).
Ketika rolled terjadi, segmen lama ditutup secara permanen menjadi berkas read-only, dan Kafka membuat berkas segmen aktif baru untuk menampung data berikutnya.
3. Log Cleaner Thread (Scheduler Pembersih) #
Broker menjalankan thread pembersih internal bernama Log Cleaner Thread yang mengeksekusi pemindaian berkala setiap:
$$\text{log.retention.check.interval.ms} = 300000 \text{ ms (5 menit)}$$
Ketika thread ini berjalan:
- Ia memindai seluruh segmen tidak aktif (inactive segments) untuk setiap partisi.
- Untuk setiap segmen tidak aktif, ia memeriksa berkas indeks waktu (
.timeindex) untuk mencari timestamp terbesar di dalam segmen tersebut. - Jika selisih antara waktu saat ini dengan timestamp terbesar di segmen tersebut lebih besar dari nilai
log.retention.ms, maka seluruh berkas segmen tersebut dinyatakan kedaluwarsa. - Broker langsung menghapus berkas log segmen fisik (
.log) beserta seluruh file indeks pendukungnya (.index,.timeindex) secara utuh dari disk OS menggunakan system call penghapusan file cepat.
Dampak Pembersihan Retensi pada Konsumen: Mengatasi OffsetOutOfRangeException
#
Sebagai developer aplikasi, kita harus memahami apa yang terjadi jika aplikasi konsumen (consumer) kita mengalami downtime yang sangat lama (misalnya, mati selama 10 hari) padahal topik dikonfigurasi dengan retensi waktu 7 hari.
Kejadian Ketika Konsumen Tertinggal #
Ketika konsumen menyala kembali dan mencoba membaca data dari offset terakhir yang dikomitnya (misal: offset 5000), broker menyadari bahwa berkas segmen yang menampung offset 5000 sudah dihapus dari disk oleh Log Cleaner Thread karena usianya sudah melewati 7 hari.
Dalam kondisi ini:
- Broker menolak permintaan baca konsumen dan melemparkan kesalahan
OffsetOutOfRangeException. - Perilaku pemulihan konsumen selanjutnya dikontrol oleh konfigurasi properti konsumen bernama
auto.offset.reset.
Penyetelan Properti auto.offset.reset
#
earliest: Konsumen akan secara otomatis memindahkan pointer bacanya ke offset terkecil yang masih tersedia di disk (misal: offset 12.000). Konsumen akan langsung membaca data baru dari sana. Konsekuensi: kita kehilangan data historis dari offset 5000 hingga 11.999.latest: Konsumen memindahkan bacanya langsung ke akhir log partisi (pesan yang paling baru masuk).none: Konsumen tidak melakukan pemulihan otomatis dan langsung melempar error ke kode aplikasi klien, memaksa tim operasional melakukan intervensi manual.
Retensi Berbasis Waktu di Era Tiered Storage (Penyimpanan Bertingkat) #
Mulai Kafka 3.0+, diperkenalkan fitur arsitektur modern bernama Tiered Storage (Penyimpanan Bertingkat). Fitur ini memisahkan antara penyimpanan lokal yang cepat (Hot Tier) dengan penyimpanan objek jarak jauh yang murah (Cold Tier, seperti AWS S3, Google Cloud Storage, atau Azure Blob).
Dengan Tiered Storage, kebijakan retensi waktu kita dibagi menjadi dua tahap:
flowchart TD
Producer["Produser"] -- "Menulis" --> Active["Active Segment (Hot Tier)"]
Active --> Inactive["Inactive Segment (Rolled)"]
Inactive -- "Setelah log.local.retention.ms" --> Cold["Cold Tier (Object Storage S3/GCS)"]
Cold -- "Setelah log.retention.ms" --> Deleted["Dihapus Permanen"]
Parameter Konfigurasi Tiered Storage #
log.local.retention.ms: Durasi waktu data harus disimpan di disk lokal (Hot Tier). Biasanya disetel sangat pendek (misal: 24 jam) untuk menghemat kapasitas NVMe/SSD lokal yang mahal.log.retention.ms: Durasi waktu penyimpanan total data (Hot Tier + Cold Tier). Misalnya disetel 1 tahun. Setelah data melewati local retention, ia akan dipindahkan secara asinkron ke S3/GCS. Data tersebut baru benar-benar dihapus secara permanen setelah melewati batas 1 tahun.
Panduan CLI Operasional #
Berikut adalah perintah command line praktis untuk mengonfigurasi retensi waktu secara dinamis di tingkat topik tanpa perlu melakukan restart broker.
1. Mengubah Retensi Topik Menjadi 3 Hari (Dalam Milidetik) #
Kita merekomendasikan konversi hari ke milidetik untuk akurasi: 3 hari = 259.200.000 milidetik.
# Mengubah properti retensi waktu topik secara dinamis
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name user-activity-logs \
--alter \
--add-config log.retention.ms=259200000
2. Mengubah Tipe Timestamp Menjadi LogAppendTime #
Untuk mengamankan kebijakan retensi waktu dari ancaman clock drift jam klien:
# Menolak CreateTime klien dan memaksa LogAppendTime server
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name user-activity-logs \
--alter \
--add-config log.message.timestamp.type=LogAppendTime
3. Menurunkan Interval Roll Segmen #
Jika kita memiliki topik dengan throughput rendah tetapi membutuhkan retensi waktu yang sangat responsif (misal: data harus hilang tepat setelah 1 jam), kita harus mempercepat rolling segmen (misal: setiap 1 jam / 3.600.000 ms) agar segmen cepat ditutup dan dapat segera dihapus oleh pembersih:
# Mempercepat roll segmen agar pembersihan tidak tertunda
kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name ephemeral-data \
--alter \
--add-config log.segment.ms=3600000 \
--add-config log.retention.ms=3600000
Ringkasan #
- Retensi Berbasis Waktu: Kebijakan default Kafka untuk membuang segmen data setelah melewati batas usia penyimpanan tertentu.
- Hierarki Parameter:
log.retention.msmemiliki prioritas tertinggi, disusullog.retention.minutes, lalulog.retention.hours.- LogAppendTime: Gunakan penyetelan ini untuk memastikan keandalan retensi waktu terbebas dari kesalahan jam sistem klien (clock drift).
- File Indeks Waktu: Kafka menggunakan berkas
.timeindexuntuk mengevaluasi umur segmen secara instan tanpa perlu memindai file log utama yang besar.- NTP Server: Pasang sinkronisasi jam Linux (misal:
chrony) pada seluruh broker untuk mencegah masalah ketidakcocokan waktu (clock drift).- OffsetOutOfRangeException: Terjadi jika konsumen offline lebih lama dari retensi topik. Atur parameter
auto.offset.resetkonsumen secara bijak.- Tiered Storage: Arsitektur modern yang memungkinkan pemindahan data secara asinkron dari disk lokal (Hot Tier) ke Cloud Object Storage (Cold Tier) berdasarkan parameter
log.local.retention.ms.