|
100 | 100 | import org.apache.pulsar.client.admin.PulsarAdminException; |
101 | 101 | import org.apache.pulsar.client.api.schema.GenericRecord; |
102 | 102 | import org.apache.pulsar.client.impl.BatchMessageIdImpl; |
| 103 | +import org.apache.pulsar.client.impl.ClientBuilderImpl; |
| 104 | +import org.apache.pulsar.client.impl.ClientCnx; |
103 | 105 | import org.apache.pulsar.client.impl.ConsumerBase; |
104 | 106 | import org.apache.pulsar.client.impl.ConsumerImpl; |
105 | 107 | import org.apache.pulsar.client.impl.MessageIdImpl; |
|
111 | 113 | import org.apache.pulsar.client.impl.TopicMessageImpl; |
112 | 114 | import org.apache.pulsar.client.impl.TypedMessageBuilderImpl; |
113 | 115 | import org.apache.pulsar.client.impl.crypto.MessageCryptoBc; |
| 116 | +import org.apache.pulsar.client.impl.metrics.InstrumentProvider; |
114 | 117 | import org.apache.pulsar.client.impl.schema.writer.AvroWriter; |
115 | 118 | import org.apache.pulsar.common.allocator.PulsarByteBufAllocator; |
116 | 119 | import org.apache.pulsar.common.api.EncryptionContext; |
117 | 120 | import org.apache.pulsar.common.api.EncryptionContext.EncryptionKey; |
| 121 | +import org.apache.pulsar.common.api.proto.CommandProducerSuccess; |
118 | 122 | import org.apache.pulsar.common.api.proto.MessageMetadata; |
119 | 123 | import org.apache.pulsar.common.api.proto.SingleMessageMetadata; |
120 | 124 | import org.apache.pulsar.common.compression.CompressionCodec; |
@@ -5433,4 +5437,66 @@ public void testBacklogAfterCreatedSubscription(boolean trimLegderBeforeGetStats |
5433 | 5437 | // cleanup |
5434 | 5438 | admin.topics().delete(topic, false); |
5435 | 5439 | } |
| 5440 | + |
| 5441 | + /** |
| 5442 | + * The internal producer of replicator will resend messages after reconnected. This test guarantees that the |
| 5443 | + * internal producer will continuously resent messages even though the client side encounters the following bugs. |
| 5444 | + * - The client side issue causes `message.metadata.numMessagesInBatch` being `0`, such as |
| 5445 | + * https://github.com/streamnative/pulsar-rs/issues/376. |
| 5446 | + * - Before the fix, the resend mechanism relies on `message.metadata.numMessagesInBatch`, after the fix, the |
| 5447 | + * producer only care about whether there are pending messages. |
| 5448 | + * see also https://github.com/apache/pulsar/pull/25106. |
| 5449 | + */ |
| 5450 | + @Test |
| 5451 | + public void testResendMessagesWhichNumMessagesInBatchIsZero() throws Exception { |
| 5452 | + final String topic = BrokerTestUtil.newUniqueName("persistent://my-property/my-ns/tp"); |
| 5453 | + final String subscriptionName = "s1"; |
| 5454 | + admin.topics().createNonPartitionedTopic(topic); |
| 5455 | + admin.topics().createSubscription(topic, subscriptionName, MessageId.earliest); |
| 5456 | + |
| 5457 | + // Create a producer which can be paused to publish. |
| 5458 | + AtomicBoolean stuckProducerReconnection = new AtomicBoolean(false); |
| 5459 | + ClientBuilderImpl clientBuilder = (ClientBuilderImpl) PulsarClient.builder().serviceUrl(lookupUrl.toString()); |
| 5460 | + PulsarClient client = InjectedClientCnxClientBuilder.create(clientBuilder, (conf, eventLoopGroup) -> |
| 5461 | + new ClientCnx(InstrumentProvider.NOOP, conf, eventLoopGroup) { |
| 5462 | + protected void handleProducerSuccess(CommandProducerSuccess success) { |
| 5463 | + if (stuckProducerReconnection.get()) { |
| 5464 | + synchronized (stuckProducerReconnection) { |
| 5465 | + super.handleProducerSuccess(success); |
| 5466 | + } |
| 5467 | + } else { |
| 5468 | + super.handleProducerSuccess(success); |
| 5469 | + } |
| 5470 | + } |
| 5471 | + }); |
| 5472 | + ProducerImpl<byte[]> producer1 = (ProducerImpl<byte[]>) client.newProducer().topic(topic) |
| 5473 | + .sendTimeout(0, TimeUnit.SECONDS) |
| 5474 | + .enableBatching(false).create(); |
| 5475 | + |
| 5476 | + // Trigger a resending by unloading topics. |
| 5477 | + AtomicReference<CompletableFuture<MessageId>> latestPublishing = new AtomicReference<>(); |
| 5478 | + synchronized (stuckProducerReconnection) { |
| 5479 | + stuckProducerReconnection.set(true); |
| 5480 | + admin.topics().unload(topic); |
| 5481 | + for (int i = 0; i < 10; i++) { |
| 5482 | + ByteBuf payload = PulsarByteBufAllocator.DEFAULT.heapBuffer(1); |
| 5483 | + MessageMetadata messageMetadata = new MessageMetadata(); |
| 5484 | + messageMetadata.setUncompressedSize(1); |
| 5485 | + MessageImpl<byte[]> message1 = MessageImpl.create(topic, null, messageMetadata, payload, |
| 5486 | + Optional.empty(), null, Schema.BYTES, 0, true, 0); |
| 5487 | + // Mock bugs, which publish messages with 0 numMessagesInBatch. |
| 5488 | + message1.getMessageBuilder().setNumMessagesInBatch(0); |
| 5489 | + latestPublishing.set(producer1.sendAsync(message1)); |
| 5490 | + } |
| 5491 | + stuckProducerReconnection.set(false); |
| 5492 | + } |
| 5493 | + |
| 5494 | + // Verify: no messages being stuck. |
| 5495 | + latestPublishing.get().get(10, TimeUnit.SECONDS); |
| 5496 | + |
| 5497 | + // cleanup. |
| 5498 | + producer1.close(); |
| 5499 | + client.close(); |
| 5500 | + admin.topics().delete(topic, false); |
| 5501 | + } |
5436 | 5502 | } |
0 commit comments