Skip to main content

Working with Time-Based Processing

1. Understanding Time Semantics

Time Type Description Use Case
Event Time Timestamp when the event occurred in the real world Ensuring correct ordering and processing of events based on their actual occurrence
Ingestion Time Timestamp when the event was received by the Kafka broker Simpler processing logic where event order matters less than arrival order
Processing Time Timestamp when the event is processed by a consumer or stream processor Fast processing without strict time ordering requirements
Note: Event time is the most precise for time-based processing, but requires accurate timestamps from producers.

Example: Using Event Time in Kafka Streams

StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> source = builder.stream("events");
KTable<String, Long> counts = source
    .selectKey((key, value) -> value.split(":")[0])
    .groupBy((key, value) -> key)
    .windowedBy(TimeWindows.of(Duration.ofMinutes(5)))
    .count();

2. Understanding Event Time

Concept Description Key Details
Event Time Timestamp when the event actually occurred Used for ordering and processing events based on their real-world occurrence
Processing Time Timestamp when the event is processed by the system Can lead to incorrect ordering if events arrive out of sequence
Ingestion Time Timestamp when the event is received by the Kafka broker Between Event Time and Processing Time in terms of accuracy

Example: Setting Event Time in a Kafka Stream

import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.Produced;

KStream<String, String> inputStream = builder.stream("input-topic");

KStream<String, String> withEventTime = inputStream
    .selectKey((key, value) -> {
        // Extract timestamp from the event payload
        long eventTimestamp = extractTimestamp(value);
        return new org.apache.kafka.common.utils.Time().milliseconds();
    })
    .withTimestamps((key, value, timestamp) -> timestamp);

withEventTime.to("output-topic", Produced.with(Serdes.String(), Serdes.String()));
Note: Event time is crucial for accurate stateful processing and windowing in Kafka Streams.
Warning: Using processing time instead of event time can lead to incorrect results in time-based aggregations.

3. Understanding Processing Time

Concept Description Notes
Event Time Timestamp when the event occurred in the source system Used for ordering and windowing logic
Processing Time Timestamp when the event is processed by Kafka Based on the system clock of the processing node
Ingestion Time Timestamp when the event is received by Kafka brokers Between Event Time and Processing Time in terms of freshness

Example: Processing Time Usage

import org.apache.kafka.streams.KafkaStreams;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.kstream.KStream;

StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> source = builder.stream("input-topic");

KStream<String, String> processed = source
    .filter((key, value) -> {
        // Use processing time for filtering or windowing
        long currentTime = System.currentTimeMillis();
        return true; // Example logic
    });

KafkaStreams streams = new KafkaStreams(builder.build(), config);
streams.start();
Note: Processing time is simpler to implement but less accurate for time-based operations compared to event time.

4. Understanding Ingestion Time

Time Type Description Usage
Ingestion Time Timestamp when a record is received by the Kafka broker Used for ordering and processing within the broker

Example: Ingestion Time in Producer Record

ProducerRecord<String, String> record = new ProducerRecord<>(
    "my-topic",
    "key1",
    "message-value"
);
Note: Ingestion time is automatically set by Kafka when a record is produced, and can be used for processing logic that depends on when the message was received.

5. Configuring Timestamp Extractors

Extractor Type Description Configuration Notes
CustomTimestampExtractor Allows custom logic to extract timestamps from records
public class CustomTimestampExtractor implements TimestampExtractor {
    public long extract(ConsumerRecord<String, String> record) {
        return record.timestamp();
    }
}
Useful for legacy systems or custom timestamp formats
LogAppendTime Uses the timestamp when the record was appended to the log log.append.time Default behavior if no timestamp is present
CreateTime Uses the timestamp provided by the producer create.time Most common for event-driven systems

Example: Using Custom Timestamp Extractor

Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "timestamp-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.TIMESTAMP_EXTRACTOR_CLASS_CONFIG, CustomTimestampExtractor.class);
Note: Timestamps are critical for time-based windowing and processing in Kafka Streams.

6. Implementing Custom Timestampers

Property Description Default Value Notes
timestamp.extractor Class name of the timestamp extractor implementation org.apache.kafka.common.utils.SystemTime Used to extract or generate timestamps for records
timestamp.type Defines how timestamps are handled (LOG_APPEND, CREATE_TIME) CREATE_TIME Controls whether to use the timestamp from the record or the broker's append time

Example: Custom Timestamp Extractor Implementation

import org.apache.kafka.common.utils.Time;
import org.apache.kafka.common.utils.SystemTime;

public class CustomTimestampExtractor implements TimestampExtractor {
    private final Time time = new SystemTime();

    @Override
    public long extract(ConsumerRecord<String, String> record, long partitionTime) {
        // Use a custom timestamp from the record's payload or metadata
        String payload = record.value();
        if (payload != null && payload.contains("timestamp")) {
            // Extract timestamp from payload (example logic)
            return parseTimestampFromPayload(payload);
        }
        return time.milliseconds(); // Fallback to current system time
    }

    private long parseTimestampFromPayload(String payload) {
        // Dummy implementation for illustration
        return System.currentTimeMillis();
    }
}
Note: The TimestampExtractor interface allows you to define custom logic for extracting timestamps from records, which is useful in scenarios where the record's inherent timestamp is not reliable or needs to be derived from other fields.

7. Using Tumbling Windows

Tumbling windows are fixed-size, non-overlapping time-based windows used to process streams of data in Kafka Streams. They are commonly used for aggregations like counts, sums, or averages over fixed time intervals.

Property Description Default Value
windowSize Duration of the tumbling window in milliseconds 60000 (1 minute)
timeWindow Time window type (e.g., TimeWindows.of(...)) TimeWindows.of(Duration.ofMinutes(1))

Example: Tumbling Window for Counting Events

StreamsBuilder builder = new StreamsBuilder();
KTable<String, Long> windowedCounts = builder.stream("input-topic")
    .groupBy((key, value) -> key)
    .windowedBy(TimeWindows.of(Duration.ofMinutes(5)))
    .count();
Note: Tumbling windows are ideal for scenarios where you want to aggregate data into fixed time buckets without overlap.

8. Using Hopping Windows

Hopping windows in Kafka Streams allow you to process data in fixed-size, overlapping time windows. They are useful for scenarios where you want to analyze data over a sliding time window with a specified hop interval.

Property Description Default Value Notes
windowSize Duration of the window in milliseconds None Must be specified
hop Time interval between window starts in milliseconds None Must be specified and less than or equal to windowSize
timeWindow Time window type (e.g., TumblingWindow, HoppingWindow) HoppingWindow Used to define the windowing strategy

Example: Hopping Window Configuration

StreamsBuilder builder = new StreamsBuilder();
KTable<String, Integer> table = builder.table("input-topic");

TimeWindows windows = TimeWindows.of(Duration.ofMinutes(5))
    .advanceBy(Duration.ofMinutes(1));

KGroupedStream<String, Integer> grouped = table.groupBy(KeyValue.pair("key", 10));
KTable<Windowed<String>, Long> windowedCount = grouped
    .windowedBy(windows)
    .count();
Note: The hop interval must be less than or equal to the window size to ensure overlapping windows.

9. Using Sliding Windows

Sliding windows in Kafka are used to process data streams over a time-based window of fixed duration. They allow you to aggregate or compute metrics over a continuous, moving time window.

Property Description Default Value Notes
window.size.ms Duration of the sliding window in milliseconds 60000 (1 minute) Must be a positive integer
window.slide.ms Slide interval in milliseconds 60000 (1 minute) Must be a positive integer, less than or equal to window size
window.type Type of window (time-based or count-based) time Valid values: time, count

Example: Sliding Window Configuration in Kafka Streams

StreamsBuilder builder = new StreamsBuilder();

KTable<String, Long> windowedCounts = builder
    .stream("input-topic")
    .groupBy((key, value) -> key)
    .windowedBy(TimeWindows.of(Duration.ofMinutes(5)).advanceBy(Duration.ofMinutes(1)))
    .count();

windowedCounts.toStream()
    .mapValues((key, value) -> String.format("Key: %s, Count: %d", key.key(), value))
    .to("output-topic");
Note: The advanceBy parameter defines how often the window slides. If set to 1 minute, a new window is created every minute, and the previous window is discarded after it expires.
Warning: Using very small slide intervals can lead to high memory usage and performance degradation due to frequent window creation and cleanup.

10. Using Session Windows

Session windows in Kafka Streams are used to group records that belong to the same session based on a user-defined session timeout. They are particularly useful for aggregating data over periods of activity followed by inactivity.

Property Description Default Value Notes
session.timeout.ms Timeout for session inactivity 300000 (5 minutes) Defines how long a session can be inactive before it's considered expired
session.window.ms Duration of session window in milliseconds 300000 (5 minutes) Used to define the window size for session windows

Example: Session Windows in Kafka Streams

StreamsBuilder builder = new StreamsBuilder();
KTable<String, Long> sessionTable = builder.table(
    "input-topic",
    Consumed.with(Serdes.String(), Serdes.Long())
).groupBy(
    (key, value) -> key,
    Grouped.with(Serdes.String(), Serdes.Long())
).windowedBy(
    SessionWindows.of(300000L)
).reduce(
    (value1, value2) -> value1 + value2
);
Note: Session windows are useful for tracking user activity over time, such as session-based analytics or user behavior tracking.

11. Configuring Grace Period

Property Default Value Description
consumer.graceful.shutdown.timeout.ms 30000 Timeout in milliseconds for graceful shutdown of consumer.
producer.graceful.shutdown.timeout.ms 30000 Timeout in milliseconds for graceful shutdown of producer.

Example: Consumer Graceful Shutdown Configuration

{
  "bootstrap.servers": "localhost:9092",
  "group.id": "my-consumer-group",
  "enable.auto.commit": false,
  "graceful.shutdown.timeout.ms": 30000
}

Example: Producer Graceful Shutdown Configuration

{
  "bootstrap.servers": "localhost:9092",
  "acks": "all",
  "retries": 3,
  "graceful.shutdown.timeout.ms": 30000
}

12. Handling Out-of-Order Events

When events arrive out of order in Kafka, handling them correctly is crucial for maintaining data consistency and accurate processing. Kafka does not guarantee message ordering within a partition, so applications must implement strategies to manage out-of-order events.

Strategy Description Use Case Notes
Event Time vs. Ingestion Time Use event timestamps (event time) instead of when messages are ingested (ingestion time) to maintain logical ordering. Time-series analytics, stream processing with temporal logic Requires timestamp extraction from messages or metadata
Watermarking Assign watermarks to track the progress of event time in streams, allowing for late arrival handling. Event-driven systems with temporal constraints Used in Apache Flink, Spark Streaming, and Kafka Streams
Windowing Group events into time-based windows to process them in batches and handle out-of-order data within window boundaries. Aggregations over time windows, real-time dashboards Supports tumbling, sliding, and session windows
State Management Use Kafka Streams or other stateful processors to maintain state and reorder events based on timestamps. Stateful stream processing, aggregations Requires careful design to avoid memory issues

Example: Using Watermarks in Kafka Streams

import org.apache.kafka.streams.*;
import org.apache.kafka.streams.kstream.*;

KafkaStreams streams = new KafkaStreams(
    builder.build(),
    new StreamsConfig(props)
);

// Define a watermark for event time
KStream<String, MyEvent> events = builder.stream("events-topic");

// Apply windowing with a 5-minute allowed lateness
KTable<String, Long> counts = events
    .groupBy(KeyValue::new)
    .windowedBy(TimeWindows.of(Duration.ofMinutes(5)).grace(Duration.ofMinutes(1)))
    .count();

streams.start();
Note: Watermarks and windowing strategies help manage out-of-order events in time-series processing pipelines. They allow systems to process data within a defined temporal boundary, ensuring that late events are still accounted for in aggregations or computations.

Was this article helpful?