|
13 | 13 |
|
14 | 14 | package com.rabbitmq.stream.impl;
|
15 | 15 |
|
| 16 | +import static com.rabbitmq.stream.impl.TestUtils.ResponseConditions.ok; |
16 | 17 | import static com.rabbitmq.stream.impl.TestUtils.b;
|
| 18 | +import static com.rabbitmq.stream.impl.TestUtils.declareSuperStreamTopology; |
| 19 | +import static com.rabbitmq.stream.impl.TestUtils.deleteSuperStreamTopology; |
17 | 20 | import static com.rabbitmq.stream.impl.TestUtils.latchAssert;
|
| 21 | +import static com.rabbitmq.stream.impl.TestUtils.streamName; |
18 | 22 | import static com.rabbitmq.stream.impl.TestUtils.waitAtMost;
|
19 | 23 | import static org.assertj.core.api.Assertions.assertThat;
|
20 | 24 |
|
21 | 25 | import com.rabbitmq.client.Connection;
|
22 | 26 | import com.rabbitmq.client.ConnectionFactory;
|
23 | 27 | import com.rabbitmq.stream.OffsetSpecification;
|
24 | 28 | import com.rabbitmq.stream.impl.Client.ClientParameters;
|
| 29 | +import com.rabbitmq.stream.impl.Client.ConsumerUpdateListener; |
25 | 30 | import com.rabbitmq.stream.impl.Client.Response;
|
26 | 31 | import java.util.HashMap;
|
| 32 | +import java.util.List; |
27 | 33 | import java.util.Map;
|
28 | 34 | import java.util.concurrent.ConcurrentHashMap;
|
29 | 35 | import java.util.concurrent.CountDownLatch;
|
30 | 36 | import java.util.concurrent.atomic.AtomicInteger;
|
31 | 37 | import java.util.concurrent.atomic.AtomicLong;
|
| 38 | +import java.util.concurrent.atomic.AtomicReference; |
| 39 | +import java.util.function.Consumer; |
| 40 | +import java.util.stream.Collectors; |
32 | 41 | import java.util.stream.IntStream;
|
33 | 42 | import org.junit.jupiter.api.Test;
|
34 | 43 | import org.junit.jupiter.api.TestInfo;
|
@@ -199,7 +208,7 @@ void singleActiveConsumerShouldRolloverWhenAnotherJoinsPartition(TestInfo info)
|
199 | 208 | Map<Byte, Boolean> consumerStates = consumerStates(2);
|
200 | 209 | AtomicLong lastReceivedOffset = new AtomicLong(0);
|
201 | 210 | Map<Byte, AtomicInteger> receivedMessages = receivedMessages(2);
|
202 |
| - String superStream = TestUtils.streamName(info); |
| 211 | + String superStream = streamName(info); |
203 | 212 | String consumerName = "foo";
|
204 | 213 | Connection c = new ConnectionFactory().newConnection();
|
205 | 214 | try {
|
@@ -299,4 +308,95 @@ void singleActiveConsumerShouldRolloverWhenAnotherJoinsPartition(TestInfo info)
|
299 | 308 | c.close();
|
300 | 309 | }
|
301 | 310 | }
|
| 311 | + |
| 312 | + @Test |
| 313 | + void singleActiveConsumersShouldSpreadOnSuperStreamPartitions(TestInfo info) throws Exception { |
| 314 | + Map<Byte, Boolean> consumerStates = consumerStates(3 * 3); |
| 315 | + String superStream = streamName(info); |
| 316 | + String consumerName = "foo"; |
| 317 | + Connection c = new ConnectionFactory().newConnection(); |
| 318 | + // client 1: 0, 1, 2 / client 2: 3, 4, 5, / client 3: 6, 7, 8 |
| 319 | + try { |
| 320 | + declareSuperStreamTopology(c, superStream, 3); |
| 321 | + List<String> partitions = |
| 322 | + IntStream.range(0, 3).mapToObj(i -> superStream + "-" + i).collect(Collectors.toList()); |
| 323 | + ConsumerUpdateListener consumerUpdateListener = |
| 324 | + (client1, subscriptionId, active) -> { |
| 325 | + System.out.println(subscriptionId + " " + active); |
| 326 | + consumerStates.put(subscriptionId, active); |
| 327 | + return null; |
| 328 | + }; |
| 329 | + Client client1 = |
| 330 | + cf.get(new ClientParameters().consumerUpdateListener(consumerUpdateListener)); |
| 331 | + Map<String, String> subscriptionProperties = new HashMap<>(); |
| 332 | + subscriptionProperties.put("single-active-consumer", "true"); |
| 333 | + subscriptionProperties.put("name", consumerName); |
| 334 | + subscriptionProperties.put("super-stream", superStream); |
| 335 | + AtomicInteger subscriptionCounter = new AtomicInteger(0); |
| 336 | + AtomicReference<Client> client = new AtomicReference<>(); |
| 337 | + Consumer<String> subscriptionCallback = |
| 338 | + partition -> { |
| 339 | + Response response = |
| 340 | + client |
| 341 | + .get() |
| 342 | + .subscribe( |
| 343 | + b(subscriptionCounter.getAndIncrement()), |
| 344 | + partition, |
| 345 | + OffsetSpecification.first(), |
| 346 | + 2, |
| 347 | + subscriptionProperties); |
| 348 | + assertThat(response).is(ok()); |
| 349 | + }; |
| 350 | + |
| 351 | + client.set(client1); |
| 352 | + partitions.forEach(subscriptionCallback); |
| 353 | + |
| 354 | + waitAtMost( |
| 355 | + () -> consumerStates.get(b(0)) && consumerStates.get(b(1)) && consumerStates.get(b(2))); |
| 356 | + |
| 357 | + Client client2 = |
| 358 | + cf.get(new ClientParameters().consumerUpdateListener(consumerUpdateListener)); |
| 359 | + |
| 360 | + client.set(client2); |
| 361 | + partitions.forEach(subscriptionCallback); |
| 362 | + |
| 363 | + waitAtMost( |
| 364 | + () -> consumerStates.get(b(0)) && consumerStates.get(b(4)) && consumerStates.get(b(2))); |
| 365 | + |
| 366 | + Client client3 = |
| 367 | + cf.get(new ClientParameters().consumerUpdateListener(consumerUpdateListener)); |
| 368 | + |
| 369 | + client.set(client3); |
| 370 | + partitions.forEach(subscriptionCallback); |
| 371 | + |
| 372 | + waitAtMost( |
| 373 | + () -> consumerStates.get(b(0)) && consumerStates.get(b(4)) && consumerStates.get(b(8))); |
| 374 | + |
| 375 | + Consumer<String> unsubscriptionCallback = |
| 376 | + partition -> { |
| 377 | + int subId = subscriptionCounter.getAndIncrement(); |
| 378 | + Response response = client.get().unsubscribe(b(subId)); |
| 379 | + assertThat(response).is(ok()); |
| 380 | + consumerStates.put(b(subId), false); |
| 381 | + }; |
| 382 | + |
| 383 | + subscriptionCounter.set(0); |
| 384 | + client.set(client1); |
| 385 | + partitions.forEach(unsubscriptionCallback); |
| 386 | + |
| 387 | + waitAtMost( |
| 388 | + () -> consumerStates.get(b(3)) && consumerStates.get(b(7)) && consumerStates.get(b(5))); |
| 389 | + |
| 390 | + client.set(client2); |
| 391 | + partitions.forEach(unsubscriptionCallback); |
| 392 | + |
| 393 | + waitAtMost( |
| 394 | + () -> consumerStates.get(b(6)) && consumerStates.get(b(7)) && consumerStates.get(b(8))); |
| 395 | + |
| 396 | + client.set(client3); |
| 397 | + partitions.forEach(unsubscriptionCallback); |
| 398 | + } finally { |
| 399 | + deleteSuperStreamTopology(c, superStream, 3); |
| 400 | + } |
| 401 | + } |
302 | 402 | }
|
0 commit comments