Kafka Streams Custom TimestampExtractor for aggregation
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Kafka Streams uses timestamps to decide how records participate in windowed aggregations. If the event time you care about lives inside the message payload instead of in Kafka record metadata, you need a custom TimestampExtractor so your aggregation windows follow business event time rather than broker time.
Why Timestamp Extraction Matters
Windowed operations such as tumbling, hopping, and session windows all depend on a timestamp. By default, Kafka Streams can use the record timestamp that Kafka stores with the message, but that may not represent when the business event actually happened.
Typical cases where event time is better:
- IoT devices send delayed measurements
- mobile clients buffer events offline
- upstream services publish historical backfills
If you aggregate by ingestion time instead of event time, your windows can become misleading.
A Custom TimestampExtractor
The extractor must return milliseconds since the Unix epoch:
Using partitionTime as a fallback is often safer than throwing when a bad record arrives, though the right policy depends on your data quality requirements.
Wiring It into Kafka Streams
You can configure the extractor globally:
With that configuration, timestamp-based operations across the topology will use the extracted event time by default.
You can also attach an extractor at the Consumed level for a specific source when you do not want one global extractor to apply to every input topic.
Example Aggregation
Here is a simple five-minute windowed count:
The important point is that the five-minute windows are now based on event.getEventTimeMillis().
Late Records and Grace Periods
Custom extraction changes what "late" means. A record can arrive now but belong to an older window if its event-time field is old.
That is why grace periods matter:
With a grace period, slightly late events can still update the intended window instead of being dropped as too late.
Validating the Extracted Timestamp
Do not assume every record contains a valid timestamp. Good extractors usually protect against:
- null payloads
- negative timestamps
- missing fields
- timestamps in seconds instead of milliseconds
A defensive version:
The last thing you want is one malformed record breaking a stateful topology unnecessarily.
Common Pitfalls
The biggest mistake is returning seconds instead of milliseconds. Kafka Streams expects epoch milliseconds, so a seconds-based timestamp silently shifts windows by a factor of one thousand.
Another issue is using event time without thinking about late data. Once you switch from broker time to payload time, out-of-order records become normal, not exceptional, and your window grace settings need to reflect that.
Finally, avoid throwing away bad records inside the extractor without observability. If the extractor falls back or drops records, log or monitor that behavior so window results do not become mysterious later.
Summary
- A custom
TimestampExtractorlets Kafka Streams aggregate by event time instead of broker timestamp. - The extractor must return epoch milliseconds.
- Use it when the true business timestamp is stored inside the record payload.
- Combine event-time extraction with appropriate grace periods for late data.
- Validate malformed or missing timestamps so one bad record does not destabilize the topology.

