|
35 | 35 | import com.rabbitmq.stream.impl.Utils.CompositeConsumerUpdateListener;
|
36 | 36 | import io.netty.channel.EventLoopGroup;
|
37 | 37 | import java.time.Duration;
|
| 38 | +import java.util.ArrayList; |
| 39 | +import java.util.Collections; |
38 | 40 | import java.util.List;
|
39 | 41 | import java.util.Map;
|
40 | 42 | import java.util.concurrent.ConcurrentHashMap;
|
@@ -728,6 +730,87 @@ void sacCustomOffsetTrackingShouldTakeOverOnRelanbancing() throws Exception {
|
728 | 730 | .isGreaterThanOrEqualTo(expectedMessageCountPerPartition - 1));
|
729 | 731 | }
|
730 | 732 |
|
| 733 | + @Test |
| 734 | + void consumerGroupsOnSameSuperStreamShouldBeIndependent() throws Exception { |
| 735 | + declareSuperStreamTopology(connection, superStream, partitionCount); |
| 736 | + int messageCount = 20_000; |
| 737 | + int consumerGroupsCount = 3; |
| 738 | + List<String> consumerNames = |
| 739 | + IntStream.range(0, consumerGroupsCount).mapToObj(i -> "my-app-" + i).collect(toList()); |
| 740 | + List<String> partitions = |
| 741 | + IntStream.range(0, partitionCount).mapToObj(i -> superStream + "-" + i).collect(toList()); |
| 742 | + Map<String, AtomicInteger> receivedMessages = new ConcurrentHashMap<>(consumerGroupsCount); |
| 743 | + Map<String, AtomicInteger> receivedMessagesPerPartitions = |
| 744 | + new ConcurrentHashMap<>(consumerGroupsCount * partitionCount); |
| 745 | + Map<String, Long> lastReceivedOffsets = new ConcurrentHashMap<>(); |
| 746 | + partitions.forEach( |
| 747 | + partition -> { |
| 748 | + lastReceivedOffsets.put(partition, 0L); |
| 749 | + receivedMessagesPerPartitions.put(partition, new AtomicInteger(0)); |
| 750 | + }); |
| 751 | + |
| 752 | + List<Consumer> consumers = new ArrayList<>(consumerGroupsCount * partitionCount); |
| 753 | + consumerNames.forEach( |
| 754 | + consumerName -> { |
| 755 | + receivedMessages.put(consumerName, new AtomicInteger(0)); |
| 756 | + partitions.forEach( |
| 757 | + partition -> { |
| 758 | + receivedMessagesPerPartitions.put(partition + consumerName, new AtomicInteger(0)); |
| 759 | + Consumer consumer = |
| 760 | + environment |
| 761 | + .consumerBuilder() |
| 762 | + .singleActiveConsumer() |
| 763 | + .superStream(superStream) |
| 764 | + .offset(OffsetSpecification.first()) |
| 765 | + .name(consumerName) |
| 766 | + .autoTrackingStrategy() |
| 767 | + .builder() |
| 768 | + .messageHandler( |
| 769 | + (context, message) -> { |
| 770 | + lastReceivedOffsets.put( |
| 771 | + context.stream() + consumerName, context.offset()); |
| 772 | + receivedMessagesPerPartitions |
| 773 | + .get(context.stream() + consumerName) |
| 774 | + .incrementAndGet(); |
| 775 | + receivedMessages.get(consumerName).incrementAndGet(); |
| 776 | + }) |
| 777 | + .build(); |
| 778 | + consumers.add(consumer); |
| 779 | + }); |
| 780 | + }); |
| 781 | + |
| 782 | + partitions.forEach( |
| 783 | + partition -> TestUtils.publishAndWaitForConfirms(cf, messageCount, partition)); |
| 784 | + |
| 785 | + int messageTotal = messageCount * partitionCount; |
| 786 | + consumerNames.forEach( |
| 787 | + consumerName -> waitUntil(() -> receivedMessages.get(consumerName).get() == messageTotal)); |
| 788 | + |
| 789 | + consumerNames.forEach( |
| 790 | + consumerName -> { |
| 791 | + // summing the received messages for the consumer group name |
| 792 | + assertThat( |
| 793 | + receivedMessagesPerPartitions.entrySet().stream() |
| 794 | + .filter(e -> e.getKey().endsWith(consumerName)) |
| 795 | + .mapToInt(e -> e.getValue().get()) |
| 796 | + .sum()) |
| 797 | + .isEqualTo(messageTotal); |
| 798 | + }); |
| 799 | + |
| 800 | + Collections.reverse(consumers); |
| 801 | + consumers.forEach(Consumer::close); |
| 802 | + |
| 803 | + Client c = cf.get(); |
| 804 | + consumerNames.forEach( |
| 805 | + consumerName -> { |
| 806 | + partitions.forEach( |
| 807 | + partition -> { |
| 808 | + assertThat(c.queryOffset(consumerName, partition).getOffset()) |
| 809 | + .isEqualTo(lastReceivedOffsets.get(partition + consumerName)); |
| 810 | + }); |
| 811 | + }); |
| 812 | + } |
| 813 | + |
731 | 814 | private static void waitUntil(CallableBooleanSupplier action) {
|
732 | 815 | try {
|
733 | 816 | waitAtMost(action);
|
|
0 commit comments