@@ -37,15 +37,16 @@ TEST(ctp_stm_state_test, advance_max_seen_epoch) {
3737 ct::cluster_epoch epoch1 (10 );
3838 ct::cluster_epoch epoch2 (20 );
3939 ct::cluster_epoch epoch3 (5 );
40+ model::term_id term (1 );
4041
41- state.advance_max_seen_epoch (epoch1);
42+ state.advance_max_seen_epoch (term, epoch1);
4243 EXPECT_EQ (state.get_max_seen_epoch ().value (), epoch1);
4344
44- state.advance_max_seen_epoch (epoch2);
45+ state.advance_max_seen_epoch (term, epoch2);
4546 EXPECT_EQ (state.get_max_seen_epoch ().value (), epoch2);
4647
4748 // Should not go backwards
48- state.advance_max_seen_epoch (epoch3);
49+ state.advance_max_seen_epoch (term, epoch3);
4950 EXPECT_EQ (state.get_max_seen_epoch ().value (), epoch2);
5051}
5152
@@ -57,26 +58,29 @@ TEST(ctp_stm_state_test, advance_epoch) {
5758
5859 state.advance_epoch (epoch1, model::offset (1 ));
5960 EXPECT_EQ (state.get_max_applied_epoch ().value (), epoch1);
60- EXPECT_EQ (state.get_max_seen_epoch ().value (), epoch1);
61+ // advance_epoch does not update the seen window
62+ EXPECT_FALSE (state.get_max_seen_epoch ().has_value ());
6163
6264 state.advance_epoch (epoch2, model::offset (2 ));
6365 EXPECT_EQ (state.get_max_applied_epoch ().value (), epoch2);
64- EXPECT_EQ (state.get_max_seen_epoch ().value (), epoch2 );
66+ EXPECT_FALSE (state.get_max_seen_epoch ().has_value () );
6567
6668 // Should not go backwards
6769 state.advance_epoch (epoch3, model::offset (3 ));
6870 EXPECT_EQ (state.get_max_applied_epoch ().value (), epoch2);
69- EXPECT_EQ (state.get_max_seen_epoch ().value (), epoch2 );
71+ EXPECT_FALSE (state.get_max_seen_epoch ().has_value () );
7072}
7173
7274TEST (ctp_stm_state_test, advance_epoch_on_a_follower) {
73- // On a follower the max_seen_epoch should also be updated
75+ // On a follower, advance_epoch only updates the applied window,
76+ // not the seen window. The seen window is only managed through
77+ // advance_max_seen_epoch on the leader path.
7478 ct::ctp_stm_state state;
7579 ct::cluster_epoch advance_epoch (20 );
7680
7781 state.advance_epoch (advance_epoch, model::offset (1 ));
7882
79- EXPECT_EQ (state.get_max_seen_epoch ().value (), advance_epoch );
83+ EXPECT_FALSE (state.get_max_seen_epoch ().has_value () );
8084 EXPECT_EQ (state.get_max_applied_epoch ().value (), advance_epoch);
8185}
8286
@@ -171,6 +175,7 @@ kafka::offset operator""_offset(unsigned long long v) {
171175
172176TEST (ctp_stm_state_test, sliding_window_issue) {
173177 ct::ctp_stm_state state;
178+ model::term_id term (1 );
174179
175180 kafka::offset hwm = 0_offset;
176181
@@ -193,17 +198,17 @@ TEST(ctp_stm_state_test, sliding_window_issue) {
193198 // get the write lock before we can start our window
194199 EXPECT_EQ (estimate_inactive_epoch (), std::nullopt );
195200 // Start our epochs at 2
196- EXPECT_FALSE (state.epoch_in_window (2_epoch));
201+ EXPECT_FALSE (state.epoch_in_window (term, 2_epoch));
197202
198203 // Write lock grabbed, max epoch can be advanced!
199- state.advance_max_seen_epoch (2_epoch);
204+ state.advance_max_seen_epoch (term, 2_epoch);
200205
201206 // Now epoch 0 is in the window
202- EXPECT_TRUE (state.epoch_in_window (2_epoch));
207+ EXPECT_TRUE (state.epoch_in_window (term, 2_epoch));
203208 // Epoch 1 is not in the window
204- EXPECT_FALSE (state.epoch_in_window (1_epoch));
209+ EXPECT_FALSE (state.epoch_in_window (term, 1_epoch));
205210 // Nor is 3
206- EXPECT_FALSE (state.epoch_in_window (3_epoch));
211+ EXPECT_FALSE (state.epoch_in_window (term, 3_epoch));
207212
208213 // Now the batch that was replicated with offset 0
209214 apply_replicated (2_epoch);
@@ -213,7 +218,7 @@ TEST(ctp_stm_state_test, sliding_window_issue) {
213218 EXPECT_EQ (estimate_inactive_epoch (), 1_epoch);
214219
215220 // Let's now add another batch at epoch 2
216- EXPECT_TRUE (state.epoch_in_window (2_epoch));
221+ EXPECT_TRUE (state.epoch_in_window (term, 2_epoch));
217222 apply_replicated (2_epoch);
218223
219224 // Reconciler now runs
@@ -222,19 +227,19 @@ TEST(ctp_stm_state_test, sliding_window_issue) {
222227 // Our epoch window hasn't moved
223228 EXPECT_EQ (estimate_inactive_epoch (), 1_epoch);
224229
225- EXPECT_FALSE (state.epoch_in_window (5_epoch));
230+ EXPECT_FALSE (state.epoch_in_window (term, 5_epoch));
226231
227232 // Epoch is bumped, our window should now be [2, 5]
228- state.advance_max_seen_epoch (5_epoch);
233+ state.advance_max_seen_epoch (term, 5_epoch);
229234
230235 // This is our new epoch
231- EXPECT_TRUE (state.epoch_in_window (5_epoch));
236+ EXPECT_TRUE (state.epoch_in_window (term, 5_epoch));
232237 // Our previous epoch is good still
233- EXPECT_TRUE (state.epoch_in_window (2_epoch));
238+ EXPECT_TRUE (state.epoch_in_window (term, 2_epoch));
234239 // And so is something in between (unlikely in real life, but just to show)
235- EXPECT_TRUE (state.epoch_in_window (3_epoch));
240+ EXPECT_TRUE (state.epoch_in_window (term, 3_epoch));
236241 // Something below is still bad
237- EXPECT_FALSE (state.epoch_in_window (1_epoch));
242+ EXPECT_FALSE (state.epoch_in_window (term, 1_epoch));
238243
239244 // Still not safe to GC, we accept stuff at epoch 0
240245 EXPECT_EQ (estimate_inactive_epoch (), 1_epoch);
@@ -250,12 +255,12 @@ TEST(ctp_stm_state_test, sliding_window_issue) {
250255 EXPECT_EQ (estimate_inactive_epoch (), 1_epoch);
251256
252257 // Now we start to replicate to the epoch to 10 (write lock grabbed)
253- state.advance_max_seen_epoch (10_epoch);
254- EXPECT_TRUE (state.epoch_in_window (10_epoch));
255- EXPECT_TRUE (state.epoch_in_window (5_epoch));
256- EXPECT_TRUE (state.epoch_in_window (8_epoch));
257- EXPECT_FALSE (state.epoch_in_window (0_epoch));
258- EXPECT_FALSE (state.epoch_in_window (4_epoch));
258+ state.advance_max_seen_epoch (term, 10_epoch);
259+ EXPECT_TRUE (state.epoch_in_window (term, 10_epoch));
260+ EXPECT_TRUE (state.epoch_in_window (term, 5_epoch));
261+ EXPECT_TRUE (state.epoch_in_window (term, 8_epoch));
262+ EXPECT_FALSE (state.epoch_in_window (term, 0_epoch));
263+ EXPECT_FALSE (state.epoch_in_window (term, 4_epoch));
259264
260265 apply_replicated (10_epoch);
261266
@@ -268,11 +273,11 @@ TEST(ctp_stm_state_test, sliding_window_issue) {
268273 EXPECT_EQ (estimate_inactive_epoch (), 4_epoch);
269274
270275 // Now we bump the window again, but haven't replicated it yet.
271- state.advance_max_seen_epoch (15_epoch);
272- EXPECT_TRUE (state.epoch_in_window (10_epoch));
273- EXPECT_TRUE (state.epoch_in_window (15_epoch));
274- EXPECT_TRUE (state.epoch_in_window (12_epoch));
275- EXPECT_FALSE (state.epoch_in_window (9_epoch));
276+ state.advance_max_seen_epoch (term, 15_epoch);
277+ EXPECT_TRUE (state.epoch_in_window (term, 10_epoch));
278+ EXPECT_TRUE (state.epoch_in_window (term, 15_epoch));
279+ EXPECT_TRUE (state.epoch_in_window (term, 12_epoch));
280+ EXPECT_FALSE (state.epoch_in_window (term, 9_epoch));
276281
277282 EXPECT_EQ (estimate_inactive_epoch (), 4_epoch);
278283
@@ -356,6 +361,7 @@ TEST(ctp_stm_state_test, l0_simulation) {
356361 };
357362 // We simulate the operations for a single partition in l0
358363 l0_simulation_state universe;
364+ model::term_id term (1 );
359365
360366 {
361367 std::vector<uploaded_l0_file_batch> batches{
@@ -399,12 +405,13 @@ TEST(ctp_stm_state_test, l0_simulation) {
399405 std::vector<std::function<void ()>> possible_operations;
400406 // If there are batches to upload, let's do it.
401407 if (!universe.uploaded_batches .empty ()) {
402- possible_operations.emplace_back ([&universe, &oplog] {
408+ possible_operations.emplace_back ([&universe, &oplog, term ] {
403409 auto batch = universe.uploaded_batches .front ();
404410 universe.uploaded_batches .pop_front ();
405- if (!universe.stm .epoch_in_window (batch.epoch )) {
406- universe.stm .advance_max_seen_epoch (batch.epoch );
407- ASSERT_TRUE (universe.stm .epoch_in_window (batch.epoch ));
411+ if (!universe.stm .epoch_in_window (term, batch.epoch )) {
412+ universe.stm .advance_max_seen_epoch (term, batch.epoch );
413+ ASSERT_TRUE (
414+ universe.stm .epoch_in_window (term, batch.epoch ));
408415 }
409416 placeholder_batch placeholder{
410417 .epoch = batch.epoch , .offset = universe.hwm ++};
0 commit comments