|
46 | 46 | import java.util.concurrent.atomic.AtomicInteger; |
47 | 47 | import java.util.concurrent.atomic.AtomicReference; |
48 | 48 | import java.util.function.BiFunction; |
| 49 | +import java.util.function.Supplier; |
49 | 50 | import lombok.AllArgsConstructor; |
50 | 51 | import lombok.Data; |
51 | 52 | import lombok.SneakyThrows; |
|
58 | 59 | import org.apache.pulsar.broker.service.persistent.GeoPersistentReplicator; |
59 | 60 | import org.apache.pulsar.broker.service.persistent.PersistentReplicator; |
60 | 61 | import org.apache.pulsar.broker.service.persistent.PersistentTopic; |
| 62 | +import org.apache.pulsar.client.admin.PulsarAdmin; |
61 | 63 | import org.apache.pulsar.client.api.Consumer; |
| 64 | +import org.apache.pulsar.client.api.Message; |
| 65 | +import org.apache.pulsar.client.api.MessageId; |
62 | 66 | import org.apache.pulsar.client.api.Producer; |
63 | 67 | import org.apache.pulsar.client.api.ProducerBuilder; |
64 | 68 | import org.apache.pulsar.client.api.PulsarClient; |
|
78 | 82 | import org.testng.Assert; |
79 | 83 | import org.testng.annotations.AfterClass; |
80 | 84 | import org.testng.annotations.BeforeClass; |
| 85 | +import org.testng.annotations.DataProvider; |
81 | 86 | import org.testng.annotations.Test; |
82 | 87 |
|
83 | 88 | @Slf4j |
@@ -809,4 +814,102 @@ public void testExpandTopicPartitionsOnNamespaceLevelReplication() throws Except |
809 | 814 | admin2.topics().deletePartitionedTopic(topicName, false); |
810 | 815 | }); |
811 | 816 | } |
| 817 | + |
| 818 | + private String getTheLatestMessage(String topic, PulsarClient client, PulsarAdmin admin) throws Exception { |
| 819 | + String dummySubscription = "s_" + UUID.randomUUID().toString().replace("-", ""); |
| 820 | + admin.topics().createSubscription(topic, dummySubscription, MessageId.earliest); |
| 821 | + Consumer<String> c = client.newConsumer(Schema.STRING).topic(topic).subscriptionName(dummySubscription) |
| 822 | + .subscribe(); |
| 823 | + String lastMsgValue = null; |
| 824 | + while (true) { |
| 825 | + Message<String> msg = c.receive(2, TimeUnit.SECONDS); |
| 826 | + if (msg == null) { |
| 827 | + break; |
| 828 | + } |
| 829 | + lastMsgValue = msg.getValue(); |
| 830 | + } |
| 831 | + c.unsubscribe(); |
| 832 | + return lastMsgValue; |
| 833 | + } |
| 834 | + |
| 835 | + enum ReplicationLevel { |
| 836 | + TOPIC_LEVEL, |
| 837 | + NAMESPACE_LEVEL; |
| 838 | + } |
| 839 | + |
| 840 | + @DataProvider(name = "replicationLevels") |
| 841 | + public Object[][] replicationLevels() { |
| 842 | + return new Object[][]{ |
| 843 | + {ReplicationLevel.TOPIC_LEVEL}, |
| 844 | + {ReplicationLevel.NAMESPACE_LEVEL} |
| 845 | + }; |
| 846 | + } |
| 847 | + |
| 848 | + @Test(dataProvider = "replicationLevels") |
| 849 | + public void testReloadWithTopicLevelGeoReplication(ReplicationLevel replicationLevel) throws Exception { |
| 850 | + final String topicName = ((Supplier<String>) () -> { |
| 851 | + if (replicationLevel.equals(ReplicationLevel.TOPIC_LEVEL)) { |
| 852 | + return BrokerTestUtil.newUniqueName("persistent://" + nonReplicatedNamespace + "/tp_"); |
| 853 | + } else { |
| 854 | + return BrokerTestUtil.newUniqueName("persistent://" + replicatedNamespace + "/tp_"); |
| 855 | + } |
| 856 | + }).get(); |
| 857 | + admin1.topics().createNonPartitionedTopic(topicName); |
| 858 | + admin2.topics().createNonPartitionedTopic(topicName); |
| 859 | + admin2.topics().createSubscription(topicName, "s1", MessageId.earliest); |
| 860 | + if (replicationLevel.equals(ReplicationLevel.TOPIC_LEVEL)) { |
| 861 | + admin1.topics().setReplicationClusters(topicName, Arrays.asList(cluster1, cluster2)); |
| 862 | + } else { |
| 863 | + pulsar1.getConfig().setTopicLevelPoliciesEnabled(false); |
| 864 | + } |
| 865 | + verifyReplicationWorks(topicName); |
| 866 | + |
| 867 | + /** |
| 868 | + * Verify: |
| 869 | + * 1. Inject an error to make the replicator is not able to work. |
| 870 | + * 2. Send one message, since the replicator does not work anymore, this message will not be replicated. |
| 871 | + * 3. Unload topic, the replicator will be re-created. |
| 872 | + * 4. Verify: the message can be replicated to the remote cluster. |
| 873 | + */ |
| 874 | + // Step 1: Inject an error to make the replicator is not able to work. |
| 875 | + Replicator replicator = broker1.getTopic(topicName, false).join().get().getReplicators().get(cluster2); |
| 876 | + replicator.terminate(); |
| 877 | + |
| 878 | + // Step 2: Send one message, since the replicator does not work anymore, this message will not be replicated. |
| 879 | + String msg = UUID.randomUUID().toString(); |
| 880 | + Producer p1 = client1.newProducer(Schema.STRING).topic(topicName).create(); |
| 881 | + p1.send(msg); |
| 882 | + p1.close(); |
| 883 | + // The result of "peek message" will be the messages generated, so it is not the same as the message just sent. |
| 884 | + Thread.sleep(3000); |
| 885 | + assertNotEquals(getTheLatestMessage(topicName, client2, admin2), msg); |
| 886 | + assertEquals(admin1.topics().getStats(topicName).getReplication().get(cluster2).getReplicationBacklog(), 1); |
| 887 | + |
| 888 | + // Step 3: Unload topic, the replicator will be re-created. |
| 889 | + admin1.topics().unload(topicName); |
| 890 | + |
| 891 | + // Step 4. Verify: the message can be replicated to the remote cluster. |
| 892 | + Awaitility.await().atMost(Duration.ofSeconds(300)).untilAsserted(() -> { |
| 893 | + log.info("replication backlog: {}", |
| 894 | + admin1.topics().getStats(topicName).getReplication().get(cluster2).getReplicationBacklog()); |
| 895 | + assertEquals(admin1.topics().getStats(topicName).getReplication().get(cluster2).getReplicationBacklog(), 0); |
| 896 | + assertEquals(getTheLatestMessage(topicName, client2, admin2), msg); |
| 897 | + }); |
| 898 | + |
| 899 | + // Cleanup. |
| 900 | + if (replicationLevel.equals(ReplicationLevel.TOPIC_LEVEL)) { |
| 901 | + admin1.topics().setReplicationClusters(topicName, Arrays.asList(cluster1)); |
| 902 | + Awaitility.await().untilAsserted(() -> { |
| 903 | + assertEquals(broker1.getTopic(topicName, false).join().get().getReplicators().size(), 0); |
| 904 | + }); |
| 905 | + admin1.topics().delete(topicName, false); |
| 906 | + admin2.topics().delete(topicName, false); |
| 907 | + } else { |
| 908 | + pulsar1.getConfig().setTopicLevelPoliciesEnabled(true); |
| 909 | + cleanupTopics(() -> { |
| 910 | + admin1.topics().delete(topicName); |
| 911 | + admin2.topics().delete(topicName); |
| 912 | + }); |
| 913 | + } |
| 914 | + } |
812 | 915 | } |
0 commit comments