Episode ini membedah konsep waktu paling penting di Flink: event time versus processing time. Kalian akan membangkitkan watermark untuk menangani data yang datang terlambat, mengenal window types tumbling, sliding, dan session, serta mengatur window trigger, allowed lateness, dan side output untuk late events.

Episode 4 sudah mengajarkan transformasi dasar dan menyentuh operator window. Episode 5 ini menaikkan level akurasi kalian: sebagian besar analitik streaming bergantung pada waktu, dan Flink menawarkan model waktu yang jauh lebih canggih daripada sekadar jam dinding mesin. Tanpa pemahaman ini, laporan kalian bisa keliru hanya karena beberapa event tiba terlambat.
Topik yang kita bedah: perbedaan event time dan processing time, cara membangkitkan watermark untuk menangani keterlambatan, tiga window type utama yaitu tumbling, sliding, dan session, serta trigger, allowed lateness, dan side output untuk late events. Setelah episode ini, kalian bisa membangun analitik berbasis waktu yang akurat meskipun data datang terlambat atau tersusun ulang.
Processing time adalah waktu saat mesin pemroses menangani event. Event time adalah waktu yang tercatat di dalam data itu sendiri, biasanya timestamp yang dibuat sumber. Bagi analitik yang harus merefleksikan kapan kejadian sungguhan terjadi — misalnya jam berapa transaksi dilakukan — event time adalah satu-satunya jawaban yang benar.
event time (waktu di dalam data) vs processing time (waktu mesin saat memproses)Agar event time bekerja, setiap event diharapkan membawa field timestamp:
{
"userId": "u-1001",
"action": "purchase",
"amount": 250000,
"eventTs": "2026-08-10T08:15:00+07:00"
}Kode di atas memakai eventTs sebagai event time. Kalian akan menunjuk field ini saat menyiapkan watermark strategy.
Watermark adalah penanda yang memberi tahu Flink: semua event dengan event time sebelum titik ini sudah dianggap datang. Strategi paling umum adalah bounded out of order — mengasumsikan event bisa datang terlambat maksimal sekian detik:
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import java.time.Duration;
WatermarkStrategy<Order> strategy =
WatermarkStrategy.<Order>forBoundedOutOfOrderness(Duration.ofSeconds(5))
.withTimestampAssigner((event, timestamp) -> event.getEventTs());forBoundedOutOfOrderness menentukan batas lateness, dan withTimestampAssigner memberi tahu Flink field mana yang menjadi event time. Semakin besar batas ini, semakin akurat hasilnya tetapi semakin lama window menunggu sebelum menembak.
Strategy dipasang saat source dibaca, sehingga seluruh pipeline memahami urutan event time:
DataStream<Order> orders = env.fromSource(
kafkaSource,
strategy,
"kafka-orders");Tanpa langkah ini, windowing berbasis event time tidak akan berfungsi — Flink tidak bisa menilai keterlambatan tanpa baseline yang jelas.
Tumbling window membagi stream menjadi jendela berukuran tetap yang tidak saling tumpang tindih. Setiap event tepat berada dalam satu window:
orders.keyBy(Order::getUserId)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.sum("amount");Sliding window bisa saling tumpang tindih — window baru dibuka setiap slide step. Cocok untuk menghitung rata-rata bergerak:
orders.keyBy(Order::getUserId)
.window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(5)))
.sum("amount");Session window mengelompokkan event yang berdekatan dan menutup diri setelah tidak ada event selama gap tertentu. Ideal untuk menganalisis sesi pengguna:
orders.keyBy(Order::getUserId)
.window(EventTimeSessionWindows.withGap(Time.minutes(30)))
.sum("amount");Pemilihan window type menentukan bentuk agregasi: tumbling untuk hitungan periodik, sliding untuk tren bergerak, dan session untuk pola aktivitas.
Trigger adalah logika yang memutuskan kapan hasil window dikeluarkan. Default bawaan Flink sudah memadai untuk sebagian besar kasus: window menembak ketika watermark melewati batas akhirnya.
Meski watermark sudah diatur, event yang sangat terlambat tetap bisa datang. Atur berapa lama window masih menerima event terlambat, lalu arahkan sisanya ke side output:
OutputTag<Order> lateTag = new OutputTag<Order>("late-orders") {};
SingleOutputStreamOperator<Order> windowed =
orders.keyBy(Order::getUserId)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.allowedLateness(Time.minutes(2))
.sideOutputLateData(lateTag)
.sum("amount");
DataStream<Order> lateOrders = windowed.getSideOutput(lateTag);
lateOrders.print();.allowedLateness memperpanjang masa hidup window dua menit setelah watermark melewatinya, dan .sideOutputLateData memisahkan event yang datang setelah itu agar tidak mengotori agregasi utama.
Aturan mainnya sederhana: makin besar lateness yang diizinkan, makin lengkap hasilnya tetapi makin lama window menunggu. Simulasikan perilaku ini dengan menjalankan job dan mengamati hasilnya:
./bin/flink run -d target/window-job.jar
./bin/flink list -aPerintah ./bin/flink run -d mengirim job dalam mode detached, dan ./bin/flink list -a menampilkan statusnya. Dari web dashboard, kalian bisa melihat kapan tiap window menembak dan seberapa banyak late event yang masuk ke side output.
Episode 5 memberi kalian kendali penuh atas waktu: membedakan event time dan processing time, membangkitkan watermark dengan forBoundedOutOfOrderness, memilih window type yang tepat, serta mengatur trigger, allowed lateness, dan side output untuk late events. Keterampilan ini menjadi fondasi hampir semua analitik streaming yang benar.
Inti yang harus dibawa pulang:
Di episode 6 selanjutnya kita akan membahas state management dan checkpointing — stateful operators dan key/value state, ValueState, ListState, MapState, dan AggregatingState, konfigurasi checkpointing beserta jaminan konsistensinya, serta restart strategies dan fault tolerance. Inilah kunci membangun job Flink yang andal dan tidak kehilangan data saat terjadi kegagalan.