From 4b20b0a78e27e11bff1360237de91b8f1869251b Mon Sep 17 00:00:00 2001 From: rdhabalia Date: Thu, 5 Mar 2020 16:44:56 -0800 Subject: [PATCH] [pulsar-client] fix deadlock on send failure --- .../api/SimpleProducerConsumerTest.java | 33 +++++++++++++++++++ .../pulsar/client/impl/ProducerImpl.java | 20 ++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java index e907197de7b1b..612b610754361 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java @@ -77,6 +77,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.pulsar.broker.service.persistent.PersistentTopic; import org.apache.pulsar.client.admin.PulsarAdminException; +import org.apache.pulsar.client.impl.ClientCnx; import org.apache.pulsar.client.impl.ConsumerImpl; import org.apache.pulsar.client.impl.MessageCrypto; import org.apache.pulsar.client.impl.MessageIdImpl; @@ -3283,4 +3284,36 @@ public void testConsumerStartMessageIdAtExpectedPos(boolean batching, boolean st consumer.close(); producer.close(); } + + /** + * It verifies that message failure successfully releases semaphore and client successfully receives + * InvalidMessageException. + * + * @throws Exception + */ + @Test + public void testReleaseSemaphoreOnFailMessages() throws Exception { + log.info("-- Starting {} test --", methodName); + + int maxPendingMessages = 10; + ProducerBuilder producerBuilder = pulsarClient.newProducer().enableBatching(false) + .blockIfQueueFull(true).maxPendingMessages(maxPendingMessages) + .topic("persistent://my-property/my-ns/my-topic2"); + + Producer producer = producerBuilder.create(); + List> futures = Lists.newArrayList(); + + // Asynchronously produce messages + byte[] message = new byte[ClientCnx.getMaxMessageSize() + 1]; + for (int i = 0; i < maxPendingMessages + 10; i++) { + Future future = producer.sendAsync(message); + try { + future.get(); + fail("should fail with InvalidMessageException"); + } catch (Exception e) { + assertTrue(e.getCause() instanceof PulsarClientException.InvalidMessageException); + } + } + log.info("-- Exiting {} test --", methodName); + } } diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ProducerImpl.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ProducerImpl.java index eb059096e254e..7e5cb3ba79036 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ProducerImpl.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ProducerImpl.java @@ -351,7 +351,7 @@ public void sendAsync(Message message, SendCallback callback) { PulsarClientException.InvalidMessageException invalidMessageException = new PulsarClientException.InvalidMessageException( format("The producer %s of the topic %s sends a %s message with %d bytes that exceeds %d bytes", producerName, topic, compressedStr, compressedSize, ClientCnx.getMaxMessageSize())); - callback.sendComplete(invalidMessageException); + completeCallbackAndReleaseSemaphore(callback, invalidMessageException); return; } } @@ -360,7 +360,7 @@ public void sendAsync(Message message, SendCallback callback) { PulsarClientException.InvalidMessageException invalidMessageException = new PulsarClientException.InvalidMessageException( format("The producer %s of the topic %s can not reuse the same message", producerName, topic)); - callback.sendComplete(invalidMessageException); + completeCallbackAndReleaseSemaphore(callback, invalidMessageException); compressedPayload.release(); return; } @@ -455,11 +455,9 @@ public void sendAsync(Message message, SendCallback callback) { } } } catch (PulsarClientException e) { - semaphore.release(); - callback.sendComplete(e); + completeCallbackAndReleaseSemaphore(callback, e); } catch (Throwable t) { - semaphore.release(); - callback.sendComplete(new PulsarClientException(t)); + completeCallbackAndReleaseSemaphore(callback, new PulsarClientException(t)); } } @@ -471,8 +469,9 @@ private boolean populateMessageSchema(MessageImpl msg, SendCallback callback) { return true; } if (!isMultiSchemaEnabled(true)) { - callback.sendComplete(new PulsarClientException.InvalidMessageException( - format("The producer %s of the topic %s is disabled the `MultiSchema`", producerName, topic))); + PulsarClientException.InvalidMessageException e = new PulsarClientException.InvalidMessageException( + format("The producer %s of the topic %s is disabled the `MultiSchema`", producerName, topic)); + completeCallbackAndReleaseSemaphore(callback, e); return false; } SchemaHash schemaHash = SchemaHash.of(msg.getSchema()); @@ -872,6 +871,11 @@ private void releaseSemaphoreForSendOp(OpSendMsg op) { semaphore.release(isBatchMessagingEnabled() ? op.numMessagesInBatch : 1); } + private void completeCallbackAndReleaseSemaphore(SendCallback callback, Exception exception) { + semaphore.release(); + callback.sendComplete(exception); + } + /** * Checks message checksum to retry if message was corrupted while sending to broker. Recomputes checksum of the * message header-payload again.