4747import java .util .Map ;
4848import java .util .Map .Entry ;
4949import java .util .Properties ;
50+ import java .util .Set ;
51+ import java .util .concurrent .ConcurrentHashMap ;
5052import java .util .concurrent .CountDownLatch ;
5153import java .util .concurrent .Executors ;
5254import java .util .concurrent .TimeUnit ;
@@ -2338,14 +2340,6 @@ public void testPauseResumeAndConsumerSeekAware() throws Exception {
23382340 AtomicBoolean first = new AtomicBoolean (true );
23392341 AtomicBoolean rebalance = new AtomicBoolean (true );
23402342 AtomicReference <ConsumerRebalanceListener > rebal = new AtomicReference <>();
2341- given (consumer .poll (any (Duration .class ))).willAnswer (i -> {
2342- Thread .sleep (50 );
2343- if (rebalance .getAndSet (false )) {
2344- rebal .get ().onPartitionsRevoked (Collections .emptyList ());
2345- rebal .get ().onPartitionsAssigned (records .keySet ());
2346- }
2347- return first .getAndSet (false ) ? consumerRecords : emptyRecords ;
2348- });
23492343 final CountDownLatch seekLatch = new CountDownLatch (7 );
23502344 willAnswer (i -> {
23512345 seekLatch .countDown ();
@@ -2354,17 +2348,32 @@ public void testPauseResumeAndConsumerSeekAware() throws Exception {
23542348 given (consumer .assignment ()).willReturn (records .keySet ());
23552349 final CountDownLatch pauseLatch1 = new CountDownLatch (2 ); // consumer, event publisher
23562350 final CountDownLatch pauseLatch2 = new CountDownLatch (2 ); // consumer, consumer
2351+ Set <TopicPartition > pausedParts = ConcurrentHashMap .newKeySet ();
23572352 willAnswer (i -> {
2353+ pausedParts .addAll (i .getArgument (0 ));
23582354 pauseLatch1 .countDown ();
23592355 pauseLatch2 .countDown ();
23602356 return null ;
23612357 }).given (consumer ).pause (records .keySet ());
2362- given (consumer .paused ()).willReturn (records .keySet ());
2358+ given (consumer .paused ()).willReturn (pausedParts );
2359+ CountDownLatch pollWhilePausedLatch = new CountDownLatch (2 );
2360+ given (consumer .poll (any (Duration .class ))).willAnswer (i -> {
2361+ Thread .sleep (50 );
2362+ if (pauseLatch1 .getCount () == 0 ) {
2363+ pollWhilePausedLatch .countDown ();
2364+ }
2365+ if (rebalance .getAndSet (false )) {
2366+ rebal .get ().onPartitionsRevoked (Collections .emptyList ());
2367+ rebal .get ().onPartitionsAssigned (records .keySet ());
2368+ }
2369+ return first .getAndSet (false ) ? consumerRecords : emptyRecords ;
2370+ });
23632371 final CountDownLatch resumeLatch = new CountDownLatch (2 );
23642372 willAnswer (i -> {
2373+ pausedParts .removeAll (i .getArgument (0 ));
23652374 resumeLatch .countDown ();
23662375 return null ;
2367- }).given (consumer ).resume (records . keySet ());
2376+ }).given (consumer ).resume (any ());
23682377 willAnswer (invoc -> {
23692378 rebal .set (invoc .getArgument (1 ));
23702379 return null ;
@@ -2456,6 +2465,8 @@ else if (e instanceof ConsumerStoppedEvent) {
24562465 assertThat (container .isPaused ()).isTrue ();
24572466 assertThat (pauseLatch1 .await (10 , TimeUnit .SECONDS )).isTrue ();
24582467 assertThat (container .isContainerPaused ()).isTrue ();
2468+ assertThat (pollWhilePausedLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
2469+ verify (consumer , never ()).resume (any ());
24592470 rebalance .set (true ); // force a re-pause
24602471 assertThat (pauseLatch2 .await (10 , TimeUnit .SECONDS )).isTrue ();
24612472 container .resume ();
@@ -2465,6 +2476,59 @@ else if (e instanceof ConsumerStoppedEvent) {
24652476 verify (consumer , times (6 )).commitSync (anyMap (), eq (Duration .ofSeconds (41 )));
24662477 }
24672478
2479+ @ SuppressWarnings ({ "unchecked" })
2480+ @ Test
2481+ public void dontResumePausedPartition () throws Exception {
2482+ ConsumerFactory <Integer , String > cf = mock (ConsumerFactory .class );
2483+ Consumer <Integer , String > consumer = mock (Consumer .class );
2484+ given (cf .createConsumer (eq ("grp" ), eq ("clientId" ), isNull (), any ())).willReturn (consumer );
2485+ ConsumerRecords <Integer , String > emptyRecords = new ConsumerRecords <>(Collections .emptyMap ());
2486+ AtomicBoolean first = new AtomicBoolean (true );
2487+ given (consumer .assignment ()).willReturn (Set .of (new TopicPartition ("foo" , 0 ), new TopicPartition ("foo" , 1 )));
2488+ final CountDownLatch pauseLatch1 = new CountDownLatch (1 );
2489+ final CountDownLatch pauseLatch2 = new CountDownLatch (2 );
2490+ Set <TopicPartition > pausedParts = ConcurrentHashMap .newKeySet ();
2491+ willAnswer (i -> {
2492+ pausedParts .addAll (i .getArgument (0 ));
2493+ pauseLatch1 .countDown ();
2494+ pauseLatch2 .countDown ();
2495+ return null ;
2496+ }).given (consumer ).pause (any ());
2497+ given (consumer .paused ()).willReturn (pausedParts );
2498+ given (consumer .poll (any (Duration .class ))).willAnswer (i -> {
2499+ Thread .sleep (50 );
2500+ return emptyRecords ;
2501+ });
2502+ final CountDownLatch resumeLatch = new CountDownLatch (1 );
2503+ willAnswer (i -> {
2504+ pausedParts .removeAll (i .getArgument (0 ));
2505+ resumeLatch .countDown ();
2506+ return null ;
2507+ }).given (consumer ).resume (any ());
2508+ ContainerProperties containerProps = new ContainerProperties (new TopicPartitionOffset ("foo" , 0 ),
2509+ new TopicPartitionOffset ("foo" , 1 ));
2510+ containerProps .setGroupId ("grp" );
2511+ containerProps .setAckMode (AckMode .RECORD );
2512+ containerProps .setClientId ("clientId" );
2513+ containerProps .setIdleEventInterval (100L );
2514+ containerProps .setMessageListener ((MessageListener ) rec -> { });
2515+ containerProps .setMissingTopicsFatal (false );
2516+ KafkaMessageListenerContainer <Integer , String > container =
2517+ new KafkaMessageListenerContainer <>(cf , containerProps );
2518+ container .start ();
2519+ InOrder inOrder = inOrder (consumer );
2520+ container .pausePartition (new TopicPartition ("foo" , 1 ));
2521+ assertThat (pauseLatch1 .await (10 , TimeUnit .SECONDS )).isTrue ();
2522+ assertThat (pausedParts ).hasSize (1 );
2523+ container .pause ();
2524+ assertThat (pauseLatch2 .await (10 , TimeUnit .SECONDS )).isTrue ();
2525+ assertThat (pausedParts ).hasSize (2 );
2526+ container .resume ();
2527+ assertThat (resumeLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
2528+ assertThat (pausedParts ).hasSize (1 );
2529+ container .stop ();
2530+ }
2531+
24682532 @ SuppressWarnings ({ "unchecked" , "rawtypes" })
24692533 @ Test
24702534 public void testInitialSeek () throws Exception {
0 commit comments