@@ -36,39 +36,63 @@ using broker_timestamp_t = ss::lowres_system_clock::time_point;
3636
3737// clang-format off
3838// this truth table shows which timestamps gets used as retention_timestamp
39+ // `validated_batch_timestamps` ensures that the `max_timestamp` of a batch
40+ // produced to `redpanda` has been validated by properties
41+ // `message.timestamp.{before/after}.max.ms` and are valid for retention enforcement.
42+ // Prior to this, the broker timestamp would be used for retention enforcement,
43+ // and even prior to this, the unvalidated `max_timestamp` would be used.
3944//
40- // use_broker_ts has_broker_ts ignore_future_ts has_alternative_to_future_ts retention_ts
45+ // use_broker_ts has_broker_ts ignore_future_ts has_alternative_to_future_ts validated_batch_timestamps retention_ts
4146// (likely false)
42- // TRUE TRUE TRUE TRUE broker_timestamp
43- // TRUE TRUE TRUE FALSE broker_timestamp
44- // TRUE TRUE FALSE TRUE broker_timestamp
45- // TRUE TRUE FALSE FALSE broker_timestamp // new segment, new cluster
46- // TRUE FALSE TRUE TRUE segment_index::_retention_ms // buggy old segment, new cluster
47- // TRUE FALSE TRUE FALSE max_timestamp
48- // TRUE FALSE FALSE TRUE max_timestamp
49- // TRUE FALSE FALSE FALSE max_timestamp // old segment, new cluster
50- // FALSE TRUE TRUE TRUE segment_index::_retention_ms
51- // FALSE TRUE TRUE FALSE max_timestamp
52- // FALSE TRUE FALSE TRUE max_timestamp
53- // FALSE TRUE FALSE FALSE max_timestamp // new segment, upgraded cluster
54- // FALSE FALSE TRUE TRUE segment_index::_retention_ms // buggy old segments
55- // FALSE FALSE TRUE FALSE max_timestamp
56- // FALSE FALSE FALSE TRUE max_timestamp
57- // FALSE FALSE FALSE FALSE max_timestamp // old segment, upgraded cluster
47+ // TRUE TRUE TRUE TRUE TRUE max_timestamp
48+ // TRUE TRUE TRUE FALSE TRUE max_timestamp
49+ // TRUE TRUE FALSE TRUE TRUE max_timestamp
50+ // TRUE TRUE FALSE FALSE TRUE max_timestamp
51+ // TRUE FALSE TRUE TRUE TRUE max_timestamp
52+ // TRUE FALSE TRUE FALSE TRUE max_timestamp
53+ // TRUE FALSE FALSE TRUE TRUE max_timestamp
54+ // TRUE FALSE FALSE FALSE TRUE max_timestamp
55+ // TRUE TRUE TRUE TRUE FALSE broker_timestamp
56+ // TRUE TRUE TRUE FALSE FALSE broker_timestamp
57+ // TRUE TRUE FALSE TRUE FALSE broker_timestamp
58+ // TRUE TRUE FALSE FALSE FALSE broker_timestamp // new segment, new cluster
59+ // TRUE FALSE TRUE TRUE FALSE segment_index::_retention_ms // buggy old segment, new cluster
60+ // TRUE FALSE TRUE FALSE FALSE max_timestamp
61+ // TRUE FALSE FALSE TRUE FALSE max_timestamp
62+ // TRUE FALSE FALSE FALSE FALSE max_timestamp // old segment, new cluster
63+ // FALSE TRUE TRUE TRUE FALSE segment_index::_retention_ms
64+ // FALSE TRUE TRUE FALSE FALSE max_timestamp
65+ // FALSE TRUE FALSE TRUE FALSE max_timestamp
66+ // FALSE TRUE FALSE FALSE FALSE max_timestamp // new segment, upgraded cluster
67+ // FALSE FALSE TRUE TRUE FALSE segment_index::_retention_ms // buggy old segments
68+ // FALSE FALSE TRUE FALSE FALSE max_timestamp
69+ // FALSE FALSE FALSE TRUE FALSE max_timestamp
70+ // FALSE FALSE FALSE FALSE FALSE max_timestamp // old segment, upgraded cluster
71+ // FALSE TRUE TRUE TRUE TRUE max_timestamp
72+ // FALSE TRUE TRUE FALSE TRUE max_timestamp
73+ // FALSE TRUE FALSE TRUE TRUE max_timestamp
74+ // FALSE TRUE FALSE FALSE TRUE max_timestamp
75+ // FALSE FALSE TRUE TRUE TRUE max_timestamp
76+ // FALSE FALSE TRUE FALSE TRUE max_timestamp
77+ // FALSE FALSE FALSE TRUE TRUE max_timestamp
78+ // FALSE FALSE FALSE FALSE TRUE max_timestamp
5879// clang-format on
5980
6081// this struct is meant to be a local copy of the feature
6182// broker_time_based_retention and configuration property
6283// storage_ignore_timestamps_in_future_secs
6384struct time_based_retention_cfg {
6485 bool use_broker_time;
86+ bool use_validated_batch_time;
6587 bool use_escape_hatch_for_timestamps_in_the_future;
6688
6789 static auto make (const features::feature_table& ft)
6890 -> time_based_retention_cfg {
6991 return {
7092 .use_broker_time = ft.is_active (
7193 features::feature::broker_time_based_retention),
94+ .use_validated_batch_time = ft.is_active (
95+ features::feature::validated_batch_timestamps),
7296 .use_escape_hatch_for_timestamps_in_the_future
7397 = config::shard_local_cfg ()
7498 .storage_ignore_timestamps_in_future_sec ()
@@ -81,7 +105,14 @@ struct time_based_retention_cfg {
81105 std::optional<model::timestamp> broker_ts,
82106 model::timestamp max_ts,
83107 std::optional<model::timestamp> alternative_retention_ts) const noexcept {
84- // new clusters and new segments should hit this branch
108+ // For versions of `redpanda` that have `max_timestamp` in batches
109+ // validated by `message.timestamp.{before/after}.max.ms` and
110+ // unconditionally set in the produce path (see:
111+ // v/kafka/server/handlers/produce_validation.cc)
112+ if (use_validated_batch_time) {
113+ return max_ts;
114+ }
115+
85116 if (likely (use_broker_time && broker_ts.has_value ())) {
86117 return *broker_ts;
87118 }
@@ -350,8 +381,10 @@ struct fmt::formatter<storage::time_based_retention_cfg>
350381 : public fmt::formatter<std::string_view> {
351382 auto format (const storage::time_based_retention_cfg& cfg, auto & ctx) const {
352383 auto str = ssx::sformat (
353- " [.use_broker_time={}, "
384+ " [.use_validated_batch_time={}, "
385+ " .use_broker_time={}, "
354386 " .use_escape_hatch_for_timestamps_in_the_future={}]" ,
387+ cfg.use_validated_batch_time ,
355388 cfg.use_broker_time ,
356389 cfg.use_escape_hatch_for_timestamps_in_the_future );
357390 return formatter<std::string_view>::format (
0 commit comments