1
1
/*
2
- * Copyright 2019-2022 the original author or authors.
2
+ * Copyright 2019-2023 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
27
27
import static org .mockito .Mockito .times ;
28
28
import static org .mockito .Mockito .verify ;
29
29
30
+ import java .util .ArrayList ;
30
31
import java .util .Arrays ;
32
+ import java .util .HashMap ;
31
33
import java .util .List ;
34
+ import java .util .Map ;
32
35
import java .util .concurrent .atomic .AtomicBoolean ;
33
36
import java .util .concurrent .atomic .AtomicReference ;
34
37
35
38
import org .apache .kafka .clients .consumer .Consumer ;
36
39
import org .apache .kafka .clients .consumer .ConsumerGroupMetadata ;
37
40
import org .apache .kafka .clients .consumer .ConsumerRecord ;
41
+ import org .apache .kafka .clients .consumer .ConsumerRecords ;
38
42
import org .apache .kafka .common .TopicPartition ;
39
43
import org .junit .jupiter .api .Test ;
40
44
import org .mockito .InOrder ;
@@ -101,7 +105,6 @@ void testClassifier() {
101
105
102
106
@ Test
103
107
void testBatchBackOff () {
104
- AtomicReference <ConsumerRecord <?, ?>> recovered = new AtomicReference <>();
105
108
@ SuppressWarnings ("unchecked" )
106
109
KafkaOperations <String , String > template = mock (KafkaOperations .class );
107
110
given (template .isTransactional ()).willReturn (true );
@@ -118,35 +121,64 @@ void testBatchBackOff() {
118
121
ConsumerRecord <String , String > record1 = new ConsumerRecord <>("foo" , 0 , 0L , "foo" , "bar" );
119
122
ConsumerRecord <String , String > record2 = new ConsumerRecord <>("foo" , 1 , 1L , "foo" , "bar" );
120
123
List <ConsumerRecord <String , String >> records = Arrays .asList (record1 , record2 );
124
+ Map <TopicPartition , List <ConsumerRecord <String , String >>> map = new HashMap <>();
125
+ records .forEach (rec -> map .computeIfAbsent (new TopicPartition (rec .topic (), rec .partition ()),
126
+ tp -> new ArrayList <>()).add (rec ));
127
+ ConsumerRecords <String , String > consumerRecords = new ConsumerRecords <>(map );
121
128
IllegalStateException illegalState = new IllegalStateException ();
122
129
@ SuppressWarnings ("unchecked" )
123
130
Consumer <String , String > consumer = mock (Consumer .class );
124
131
given (consumer .groupMetadata ()).willReturn (new ConsumerGroupMetadata ("foo" ));
125
132
MessageListenerContainer container = mock (MessageListenerContainer .class );
126
133
given (container .isRunning ()).willReturn (true );
127
- processor .process ( records , consumer , container , illegalState , false , EOSMode .V2 );
128
- processor .process ( records , consumer , container , illegalState , false , EOSMode .V2 );
134
+ processor .processBatch ( consumerRecords , records , consumer , container , illegalState , true , EOSMode .V2 );
135
+ processor .processBatch ( consumerRecords , records , consumer , container , illegalState , true , EOSMode .V2 );
129
136
verify (backOff , times (2 )).start ();
130
137
verify (execution .get (), times (2 )).nextBackOff ();
131
138
processor .clearThreadState ();
132
- processor .process ( records , consumer , container , illegalState , false , EOSMode .V2 );
139
+ processor .processBatch ( consumerRecords , records , consumer , container , illegalState , true , EOSMode .V2 );
133
140
verify (backOff , times (3 )).start ();
141
+
134
142
}
135
143
136
- void testEarlyExitBackOff () {
137
- DefaultAfterRollbackProcessor <String , String > processor = new DefaultAfterRollbackProcessor <>(
138
- new FixedBackOff (1 , 10_000 ));
144
+ @ Test
145
+ void testBatchListenerFailedException () {
139
146
@ SuppressWarnings ("unchecked" )
140
- Consumer <String , String > consumer = mock (Consumer .class );
147
+ KafkaOperations <String , String > template = mock (KafkaOperations .class );
148
+ given (template .isTransactional ()).willReturn (true );
149
+ BackOff backOff = spy (new FixedBackOff (0 , 1 ));
150
+ AtomicReference <BackOffExecution > execution = new AtomicReference <>();
151
+ willAnswer (inv -> {
152
+ BackOffExecution exec = spy ((BackOffExecution ) inv .callRealMethod ());
153
+ execution .set (exec );
154
+ return exec ;
155
+ }).given (backOff ).start ();
156
+ ConsumerRecordRecoverer recoverer = mock (ConsumerRecordRecoverer .class );
157
+ DefaultAfterRollbackProcessor <String , String > processor = new DefaultAfterRollbackProcessor <>(recoverer ,
158
+ backOff , template , false );
141
159
ConsumerRecord <String , String > record1 = new ConsumerRecord <>("foo" , 0 , 0L , "foo" , "bar" );
160
+ ConsumerRecord <String , String > record3 = new ConsumerRecord <>("foo" , 0 , 1L , "foo" , "bar" );
142
161
ConsumerRecord <String , String > record2 = new ConsumerRecord <>("foo" , 1 , 1L , "foo" , "bar" );
143
- List <ConsumerRecord <String , String >> records = Arrays .asList (record1 , record2 );
144
- IllegalStateException illegalState = new IllegalStateException ();
162
+ ConsumerRecord <String , String > record4 = new ConsumerRecord <>("foo" , 1 , 2L , "foo" , "bar" );
163
+ List <ConsumerRecord <String , String >> records = Arrays .asList (record1 , record2 , record3 , record4 );
164
+ Map <TopicPartition , List <ConsumerRecord <String , String >>> map = new HashMap <>();
165
+ records .forEach (rec -> map .computeIfAbsent (new TopicPartition (rec .topic (), rec .partition ()),
166
+ tp -> new ArrayList <>()).add (rec ));
167
+ ConsumerRecords <String , String > consumerRecords = new ConsumerRecords <>(map );
168
+ @ SuppressWarnings ("unchecked" )
169
+ Consumer <String , String > consumer = mock (Consumer .class );
170
+ given (consumer .groupMetadata ()).willReturn (new ConsumerGroupMetadata ("foo" ));
145
171
MessageListenerContainer container = mock (MessageListenerContainer .class );
146
- given (container .isRunning ()).willReturn (false );
147
- long t1 = System .currentTimeMillis ();
148
- processor .process (records , consumer , container , illegalState , true , EOSMode .V2 );
149
- assertThat (System .currentTimeMillis () < t1 + 5_000 );
172
+ ContainerProperties containerProperties = mock (ContainerProperties .class );
173
+ given (container .getContainerProperties ()).willReturn (containerProperties );
174
+ given (container .isRunning ()).willReturn (true );
175
+ BatchListenerFailedException batchFailed = new BatchListenerFailedException ("" , 2 );
176
+ processor .processBatch (consumerRecords , records , consumer , container , batchFailed , true , EOSMode .V2 );
177
+ verify (consumer ).seek (new TopicPartition ("foo" , 1 ), 1 );
178
+ BatchListenerFailedException batchFailed2 = new BatchListenerFailedException ("" , record1 );
179
+ processor .processBatch (consumerRecords , records , consumer , container , batchFailed2 , true , EOSMode .V2 );
180
+ verify (consumer ).seek (new TopicPartition ("foo" , 0 ), 0 );
181
+ verify (consumer , times (2 )).seek (new TopicPartition ("foo" , 1 ), 1 );
150
182
}
151
183
152
184
@ Test
0 commit comments