Topic Design #

Di dalam ekosistem Apache Kafka, Topic (topik) adalah kategori logis atau wadah pembungkus aliran pesan (event stream). Meskipun membuat topik terdengar seperti tugas administratif yang sangat sederhana, rancangan struktur dan konfigurasi topik sebenarnya adalah keputusan arsitektural penting yang menentukan performa, keamanan, dan skalabilitas seluruh pipa data kita. Desain topik yang buruk — seperti penamaan yang tidak standar, alokasi partisi yang tidak terencana, atau kesalahan penyetelan kebijakan retensi data — dapat memicu kekacauan operasional, kebocoran memori, hingga hilangnya data kritis di lingkungan produksi.

Melalui artikel ini, kita akan mengupas secara mendalam aturan penamaan topik standar industri berbasis domain (domain-driven topic naming), membedah penyetelan kebijakan retensi data berbasis waktu dan ukuran, memilih kebijakan pembersihan (cleanup policy) yang tepat antara delete vs compact, serta merumuskan panduan terstruktur pembuatan topik skala produksi yang aman.


Standarisasi Penamaan Topik (Topic Naming Conventions) #

Salah satu masalah operasional yang paling sering terjadi pada kluster Kafka perusahaan besar adalah penumpukan ratusan topik dengan nama yang tidak teratur, seperti test-topic, logs, data-baru, atau my_topic_123. Penamaan yang serampangan ini menyulitkan tim infrastruktur dalam memantau statistik kluster, menerapkan aturan keamanan (ACLs), dan memilah data mana yang masih digunakan atau yang sudah kedaluwarsa.

Untuk merancang kluster yang bersih dan mudah dikelola, kita wajib menerapkan standarisasi penamaan berbasis Domain-Driven Design. Format penamaan standar industri yang direkomendasikan adalah:

$$\text{Format: } \langle\text{domain}\rangle.\langle\text{subdomain}\rangle.\langle\text{entity}\rangle.\langle\text{event-name}\rangle$$

Mari kita bedah masing-masing komponen di atas:

  • domain — Representasi area bisnis tingkat tertinggi (misalnya, payment, customer, telemetry, logistic).
  • subdomain — Representasi sub-sistem atau konteks terbatas (bounded context) di dalam domain tersebut (misalnya, billing, verification, iot, shipping).
  • entity — Nama objek bisnis utama yang diwakili oleh data tersebut (misalnya, invoice, password, sensor, package).
  • event-name — Nama kejadian spesifik yang mewakili fakta sejarah pesan, biasanya ditulis dalam bentuk kata kerja lampau (past-tense verb) untuk menunjukkan event stream (misalnya, created, changed, logged, delivered).

Contoh Penamaan Topik yang Baik: #

  1. payment.billing.invoice.created — Aliran data faktur tagihan yang baru saja terbit di sub-sistem pembayaran.
  2. customer.identity.password.changed — Pelaporan kejadian pengguna mengubah kata sandi untuk kebutuhan keamanan.
  3. telemetry.sensor.temperature.logged — Log data pembacaan suhu dari perangkat IoT.

Baturan Penamaan Internal System Topics #

Satu aturan mutlak yang wajib kita patuhi adalah jangan pernah membuat topik aplikasi kita dengan awalan tanda garis bawah ganda (__). Nama topik yang diawali dengan dua garis bawah dicadangkan khusus untuk kebutuhan sistem internal Apache Kafka.

Contoh topik internal bawaan Kafka meliputi:

  • __consumer_offsets — Menyimpan riwayat offset commit konsumen.
  • __transaction_state — Menyimpan status transaksi asinkron klien.
  • _schemas — Digunakan oleh Confluent Schema Registry untuk mengelola skema data.

Aturan Penulisan Tambahan: #

  • Gunakan Huruf Kecil (Lowercase): Selalu gunakan huruf kecil untuk menghindari ambiguitas pengetikan. Kafka bersifat case-sensitive, sehingga topik Payment dan payment dianggap sebagai dua topik yang berbeda.
  • Gunakan Tanda Hubung (Hyphen): Jika nama entity atau event terdiri dari dua kata, gunakan tanda hubung - untuk memisahkannya (misalnya, invoice-created). Hindari penggunaan karakter titik . untuk pemisah kata karena titik didedikasikan sebagai pembatas tingkat domain logis.
  • Jangan Masukkan Nama Lingkungan (Environment): Hindari menyertakan kata dev, staging, atau prod ke dalam nama topik (misalnya, prod.payment.billing.invoice-created). Lingkungan harus dipisahkan di tingkat kluster fisik atau namespace logical kluster, bukan di dalam nama topik itu sendiri. Menyertakan lingkungan di nama topik memaksa kita mengubah konfigurasi kode aplikasi ketika bermigrasi lintas lingkungan.

Kebijakan Retensi Data: Waktu vs Ukuran #

Berbeda dengan sistem antrean pesan tradisional (message queue) yang langsung menghapus pesan sesaat setelah dibaca oleh konsumen, Apache Kafka mempertahankan pesan di dalam disk lokal broker. Masa hidup pesan ini dikontrol melalui konfigurasi retensi data.

Kita dapat menyetel retensi data berdasarkan dua parameter utama:

1. Retensi Berbasis Waktu (retention.ms) #

Parameter ini menentukan berapa lama sebuah pesan akan disimpan di dalam segmen log broker sebelum memenuhi syarat untuk dihapus. Nilai bawaan (default) untuk parameter ini adalah 604800000 milidetik (7 hari).

  • Tuning untuk Data Sensitif: Untuk data transaksi finansial atau log audit yang membutuhkan penelusuran sejarah panjang (event sourcing), kita bisa menaikkan nilai ini menjadi 30 hari, 1 tahun, atau bahkan tidak terbatas (-1).
  • Tuning untuk Data Bervolume Tinggi: Untuk data telemetri IoT atau metrik performa aplikasi yang nilainya cepat kedaluwarsa, kita disarankan menurunkan nilai ini menjadi 1 hari (86400000 ms) atau beberapa jam saja untuk menghemat kapasitas disk broker.

2. Retensi Berbasis Ukuran (retention.bytes) #

Parameter ini menentukan kapasitas penyimpanan maksimum dalam satuan byte untuk satu partisi (bukan total ukuran topik secara keseluruhan!). Nilai default bawaan adalah -1 (tidak terbatas).

  • Jika kita menyetel parameter ini ke 1073741824 byte (1 GB) pada topik yang memiliki 3 partisi, maka kapasitas log maksimum topik tersebut di broker adalah 3 GB.
  • Ketika ukuran log pada salah satu partisi melampaui 1 GB, Kafka akan segera menghapus file segmen log tertua, meskipun waktu retensi retention.ms untuk pesan di segmen tersebut belum habis.
  • Perlindungan Kapasitas Disk: Penyetelan parameter ini wajib dilakukan di lingkungan produksi sebagai jaring pengaman agar disk broker tidak penuh (disk space protection) akibat lonjakan volume pesan yang tidak terduga.

Jebakan Segment Rolling yang Sering Membingungkan #

Banyak developer mengalami kebingungan ketika data di dalam topiknya tidak terhapus padahal waktu retensi retention.ms sudah terlampaui (misalnya disetel 1 hari, namun data 3 hari lalu masih ada). Mengapa ini terjadi?

Kafka hanya melakukan pembersihan data pada segmen log yang sudah ditutup (closed segment). Kafka tidak akan pernah menghapus data dari segmen log yang masih aktif menerima penulisan (active segment).

Dua parameter yang mengontrol penutupan segmen adalah:

  • log.segment.bytes (default 1 GB) — Segmen ditutup jika ukurannya mencapai 1 GB.
  • log.roll.hours (default 7 hari / 168 jam) — Segmen ditutup jika umurnya mencapai 7 hari.

Jika volume data kita sangat kecil, berkas segmen aktif mungkin membutuhkan waktu 7 hari untuk mencapai ukuran 1 GB atau menyentuh batas waktu 7 hari. Selama 7 hari tersebut, segmen tetap aktif terbuka, dan pesan-pesan di dalamnya tidak akan pernah dihapus oleh sistem, meskipun kita menyetel retention.ms sebesar 1 hari.

Untuk mengatasinya, jika kita menyetel retensi waktu yang sangat singkat, kita wajib mengimbangi penyetelan parameter log.roll.hours ke nilai yang lebih kecil (misalnya 2 jam) agar segmen cepat ditutup dan dibersihkan.


Cleanup Policy: Delete vs Compact #

Dalam mendesain topik, kita harus memilih bagaimana broker memperlakukan data sejarah yang sudah melewati batas retensi melalui konfigurasi cleanup.policy:

1. cleanup.policy = delete (Default) #

Ini adalah kebijakan pembersihan standar. Ketika segmen log melewati batas retensi waktu atau ukuran, seluruh berkas segmen fisik tertua tersebut akan dihapus dari disk broker secara langsung.

  • Use Case: Paling cocok untuk data aliran transaksi harian, log pelacakan aktivitas klik (clickstream), metrik infrastruktur, dan event temporal lainnya yang masa berlakunya terbatas.

2. cleanup.policy = compact (Log Compaction) #

Kebijakan ini mengaktifkan fitur Log Compaction. Jika fitur ini aktif, Kafka menjamin bahwa untuk setiap kunci (Key) pesan yang unik, broker akan selalu mempertahankan minimal satu pesan terakhir (latest state) di dalam log. Pesan-pesan lama dengan kunci yang sama akan dibersihkan oleh thread latar belakang Log Cleaner.

Proses kompresi ini bekerja di latar belakang berdasarkan konfigurasi min.cleanable.dirty.ratio (secara default bernilai 0.5 atau 50%). Ini berarti proses pembersihan baru akan berjalan setelah jumlah pesan baru (kotor) di dalam log mencapai minimal 50% dari total ukuran log terkompresi.

  • Pembersihan dengan Tombstone: Jika kita ingin menghapus sebuah kunci secara permanen pada topik terkompresi, kita harus mengirim pesan dengan kunci tersebut dan menyetel nilainya (Value) ke null. Pesan penanda ini disebut Tombstone Message. Kafka akan menyebarkan tombstone ini ke seluruh konsumen, lalu menghapus kunci tersebut dari disk pada siklus pembersihan berikutnya.
  • Use Case: Sangat ideal untuk menyimpan data snapshot status terakhir, seperti tabel profil pengguna (menyimpan alamat terakhir berdasarkan User ID), status inventaris barang (menyimpan stok terakhir berdasarkan SKU produk), atau saldo rekening nasabah.

Visualisasi Struktur Topik, Partisi, dan Broker #

Untuk memberikan pemahaman konkret tentang bagaimana topik domain-driven terbagi menjadi partisi fisik dan didistribusikan secara redundan ke seluruh broker kluster, perhatikan diagram visual berikut:

flowchart TD
    subgraph LogicalView ["Pandangan Logis (Aplikasi Klien)"]
        Topic["Topik: payment.billing.invoice.created <br/> (Replication Factor = 2)"]
    end

    subgraph PhysicalView ["Pandangan Fisik (Kluster Broker)"]
        direction LR
        subgraph Broker1 ["Broker Server 1"]
            P0_L["Partisi 0 <br/> (Leader)"]
            P1_F["Partisi 1 <br/> (Follower)"]
        end
        subgraph Broker2 ["Broker Server 2"]
            P1_L["Partisi 1 <br/> (Leader)"]
            P0_F["Partisi 0 <br/> (Follower)"]
        end
    end

    Topic -->|Partisi 0| P0_L
    Topic -->|Partisi 1| P1_L

    P0_L -. Replikasi .-> P0_F
    P1_L -. Replikasi .-> P1_F

    style Topic fill:#ddffdd,stroke:#88ff88
    style P0_L fill:#ddffdd,stroke:#88ff88
    style P1_L fill:#ddffdd,stroke:#88ff88

Kesalahan Umum (Anti-pattern) dalam Desain Topik #

Berikut adalah beberapa pola kesalahan fatal dalam pembuatan dan pengelolaan topik di industri beserta solusi perbaikannya:

1. Mengaktifkan Pembuatan Topik Otomatis (auto.create.topics.enable = true) #

Konfigurasi broker default auto.create.topics.enable bernilai true. Hal ini memungkinkan broker membuat topik baru secara instan saat ada klien yang menulis atau membaca dari nama topik yang belum terdaftar.

Konsekuensi Kegagalan: Jika pengembang aplikasi kita salah mengetik nama topik di dalam kode (misalnya menulis payment.billing.invoice.creaated alih-alih payment.billing.invoice.created), Kafka akan membuat topik baru secara otomatis dengan konfigurasi default broker (biasanya hanya memiliki 1 partisi dan faktor replikasi 1).

  • Pesan-pesan baru akan masuk ke topik typo tersebut tanpa disadari.
  • Konsumen resmi yang mendengarkan topik asli tidak akan pernah menerima data tersebut.
  • Topik typo tersebut tidak memiliki toleransi kegagalan (replikasi = 1), sehingga jika broker mati, data tersebut hilang selamanya.
  • Matikan pembuatan otomatis ini di lingkungan produksi dengan menyetel auto.create.topics.enable = false, dan kelola pembuatan topik secara deklaratif melalui skrip otomasi.

2. Skenario Kode: Membuat Topik Secara Aman Melalui Admin Client #

Alih-alih mengandalkan pembuatan otomatis atau membuat topik secara manual lewat command-line terminal yang rawan salah ketik, kita disarankan untuk membuat topik menggunakan Java AdminClient API sebagai bagian dari alur otomatisasi deployment (CI/CD) aplikasi.

Berikut perbandingan implementasi penanganan pembuatan topik yang salah dan yang benar:

// =========================================================================
// ANTI-PATTERN: Mengandalkan auto.create.topics.enable di Produksi
// Klien langsung mengirim data ke topik tanpa validasi pembuatan formal.
// =========================================================================
public void sendToUncreatedTopic(KafkaProducer<String, String> producer, String payload) {
    // ✗ JANGAN: Mengirim langsung ke nama topik acak. Jika auto-create aktif,
    // broker akan membuat topik dengan spesifikasi minimum default (Sangat Berbahaya!).
    ProducerRecord<String, String> record = new ProducerRecord<>("my-dirty-logs-topic", payload);
    producer.send(record);
}

// =========================================================================
// SOLUSI YANG BENAR: Inisialisasi Topik Formal Menggunakan AdminClient
// Topik dideklarasikan secara terprogram dengan parameter kapasitas yang aman.
// =========================================================================
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.common.config.TopicConfig;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutionException;

public class TopicProvisioner {
    private final AdminClient adminClient;

    public TopicProvisioner(String bootstrapServers) {
        Properties props = new Properties();
        props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        this.adminClient = AdminClient.create(props);
    }

    public void createStandardTopic(String topicName, int numPartitions, short replicationFactor) {
        // ✓ BENAR: Tentukan konfigurasi retensi secara eksplisit demi keamanan disk
        Map<String, String> topicConfigs = new HashMap<>();
        topicConfigs.put(TopicConfig.RETENTION_MS_CONFIG, "2592000000"); // Retensi waktu: 30 Hari
        topicConfigs.put(TopicConfig.RETENTION_BYTES_CONFIG, "10737418240"); // Retensi ukuran: 10 GB per partisi
        topicConfigs.put(TopicConfig.CLEANUP_POLICY_CONFIG, TopicConfig.CLEANUP_POLICY_DELETE);

        NewTopic newTopic = new NewTopic(topicName, numPartitions, replicationFactor)
                .configs(topicConfigs);

        try {
            // Jalankan pembuatan topik secara sinkronous
            adminClient.createTopics(Collections.singletonList(newTopic)).all().get();
            System.out.printf("✓ Sukses membuat topik terstandarisasi: %s (Partisi: %d, Replika: %d)\n", 
                topicName, numPartitions, replicationFactor);
        } catch (InterruptedException | ExecutionException e) {
            if (e.getCause() instanceof org.apache.kafka.common.errors.TopicExistsException) {
                System.out.println("Topik sudah terdaftar, melewati langkah pembuatan.");
            } else {
                System.err.println("Gagal membuat topik: " + e.getMessage());
            }
        }
    }

    public void close() {
        adminClient.close();
    }
}

Dengan menerapkan kode provisi topik di atas, kluster kita dijamin hanya akan menjalankan topik-topik yang memiliki konfigurasi kapasitas partisi yang matang dan faktor replikasi yang aman untuk toleransi kegagalan.


Ringkasan #

  • Domain-Driven Naming — Terapkan standardisasi penamaan topik industri berbasis domain bisnis: [domain].[subdomain].[entity].[event-name] untuk ketertiban administrasi.
  • retention.ms — Parameter pengontrol waktu simpan pesan di broker (default 7 hari), wajib disesuaikan berdasarkan karakteristik sensitivitas data bisnis.
  • retention.bytes — Batas memori maksimum per partisi di disk broker (default -1). Berperan sebagai jaring pengaman agar disk server tidak penuh akibat lonjakan pesan.
  • cleanup.policy=delete — Menghapus seluruh file segmen biner log yang telah melewati batas retensi untuk mengosongkan ruang disk secara cepat.
  • cleanup.policy=compact — Mengaktifkan Log Compaction untuk mempertahankan pesan status terbaru untuk setiap kunci pesan, membuang riwayat sejarah perubahan lama.
  • auto.create.topics.enable=false — Konfigurasi wajib di broker produksi untuk mencegah pembuatan topik default otomatis akibat kesalahan ketik nama topik di sisi aplikasi klien.
  • AdminClient API — Gunakan API terprogram untuk mendeklarasikan pembuatan topik baru secara otomatis dan konsisten sebagai bagian dari pipeline penyebaran kode.

← Sebelumnya: Zookeeper vs KRaft   Berikutnya: Partition Strategy →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact