You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by GitBox <gi...@apache.org> on 2021/07/01 14:29:32 UTC

[GitHub] [pulsar] codelipenghui opened a new pull request #11183: Fix the dead lock when using hasMessageAvailableAsync and readNextAsync

codelipenghui opened a new pull request #11183:
URL: https://github.com/apache/pulsar/pull/11183


   The issue will happens after satisfying the following conditions:
   
   1. The messages are added to the incoming queue before reading messages
   2. The result future of the readNextAsync been complete before call future.whenComplete by users,
      This won't always appear.
   
    After that, since we are using the IO thread to call the callback of the hasMessageAvailableAsync,
    so the IO thread will process the message.getValue(). Then might get a deadlock as followings:
   
    ```
    jdk.internal.misc.Unsafe.park(boolean, long) Unsafe.java (native)
    java.util.concurrent.locks.LockSupport.park(Object) LockSupport.java:194
    java.util.concurrent.CompletableFuture$Signaller.block() CompletableFuture.java:1796
    java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool$ManagedBlocker) ForkJoinPool.java:3128
    java.util.concurrent.CompletableFuture.waitingGet(boolean) CompletableFuture.java:1823
    java.util.concurrent.CompletableFuture.get() CompletableFuture.java:1998
    org.apache.pulsar.client.impl.schema.reader.AbstractMultiVersionReader.getSchemaInfoByVersion(byte[]) AbstractMultiVersionReader.java:115
    org.apache.pulsar.client.impl.schema.reader.MultiVersionAvroReader.loadReader(BytesSchemaVersion) MultiVersionAvroReader.java:47
    org.apache.pulsar.client.impl.schema.reader.AbstractMultiVersionReader$1.load(BytesSchemaVersion) AbstractMultiVersionReader.java:52
    org.apache.pulsar.client.impl.schema.reader.AbstractMultiVersionReader$1.load(Object) AbstractMultiVersionReader.java:49
    com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(Object, CacheLoader) LocalCache.java:3529
    com.google.common.cache.LocalCache$Segment.loadSync(Object, int, LocalCache$LoadingValueReference, CacheLoader) LocalCache.java:2278
    com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(Object, int, CacheLoader) LocalCache.java:2155
    com.google.common.cache.LocalCache$Segment.get(Object, int, CacheLoader) LocalCache.java:2045
    com.google.common.cache.LocalCache.get(Object, CacheLoader) LocalCache.java:3951
    com.google.common.cache.LocalCache.getOrLoad(Object) LocalCache.java:3974
    com.google.common.cache.LocalCache$LocalLoadingCache.get(Object) LocalCache.java:4935
    org.apache.pulsar.client.impl.schema.reader.AbstractMultiVersionReader.read(byte[], byte[]) AbstractMultiVersionReader.java:86
    org.apache.pulsar.client.impl.schema.AbstractStructSchema.decode(byte[], byte[]) AbstractStructSchema.java:60
    org.apache.pulsar.client.impl.MessageImpl.getValue() MessageImpl.java:301
    org.apache.pulsar.broker.service.SystemTopicBasedTopicPoliciesService.refreshTopicPoliciesCache(Message) SystemTopicBasedTopicPoliciesService.java:302
    org.apache.pulsar.broker.service.SystemTopicBasedTopicPoliciesService.lambda$initPolicesCache$7(SystemTopicClient$Reader, Throwable, CompletableFuture, Message, Throwable) SystemTopicBasedTopicPoliciesService.java:254
    org.apache.pulsar.broker.service.SystemTopicBasedTopicPoliciesService$$Lambda$817.accept(Object, Object)
    java.util.concurrent.CompletableFuture.uniWhenComplete(Object, BiConsumer, CompletableFuture$UniWhenComplete) CompletableFuture.java:859
    java.util.concurrent.CompletableFuture.uniWhenCompleteStage(Executor, BiConsumer) CompletableFuture.java:883
    java.util.concurrent.CompletableFuture.whenComplete(BiConsumer) CompletableFuture.java:2251
    org.apache.pulsar.broker.service.SystemTopicBasedTopicPoliciesService.lambda$initPolicesCache$10(SystemTopicClient$Reader, CompletableFuture, Boolean, Throwable) SystemTopicBasedTopicPoliciesService.java:246
    org.apache.pulsar.broker.service.SystemTopicBasedTopicPoliciesService$$Lambda$725.accept(Object, Object)
    java.util.concurrent.CompletableFuture.uniWhenComplete(Object, BiConsumer, CompletableFuture$UniWhenComplete) CompletableFuture.java:859
    java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(int) CompletableFuture.java:837
    java.util.concurrent.CompletableFuture.postComplete() CompletableFuture.java:506
    java.util.concurrent.CompletableFuture.complete(Object) CompletableFuture.java:2073
    org.apache.pulsar.client.impl.ConsumerImpl.lambda$hasMessageAvailableAsync$48(CompletableFuture, MessageId) ConsumerImpl.java:2075
    org.apache.pulsar.client.impl.ConsumerImpl$$Lambda$723.accept(Object)
    java.util.concurrent.CompletableFuture$UniAccept.tryFire(int) CompletableFuture.java:714
    java.util.concurrent.CompletableFuture.postComplete() CompletableFuture.java:506
    java.util.concurrent.CompletableFuture.complete(Object) CompletableFuture.java:2073
    org.apache.pulsar.client.impl.ConsumerImpl.lambda$internalGetLastMessageIdAsync$53(CompletableFuture, PulsarApi$CommandGetLastMessageIdResponse) ConsumerImpl.java:2187
    org.apache.pulsar.client.impl.ConsumerImpl$$Lambda$720.accept(Object)
    java.util.concurrent.CompletableFuture$UniAccept.tryFire(int) CompletableFuture.java:714
    java.util.concurrent.CompletableFuture.postComplete() CompletableFuture.java:506
    java.util.concurrent.CompletableFuture.complete(Object) CompletableFuture.java:2073
    org.apache.pulsar.client.impl.ClientCnx.handleGetLastMessageIdSuccess(PulsarApi$CommandGetLastMessageIdResponse) ClientCnx.java:468
    org.apache.pulsar.common.protocol.PulsarDecoder.channelRead(ChannelHandlerContext, Object) PulsarDecoder.java:342
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(Object) AbstractChannelHandlerContext.java:379
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) AbstractChannelHandlerContext.java:365
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(Object) AbstractChannelHandlerContext.java:357
    io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ChannelHandlerContext, CodecOutputList, int) ByteToMessageDecoder.java:324
    io.netty.handler.codec.ByteToMessageDecoder.channelRead(ChannelHandlerContext, Object) ByteToMessageDecoder.java:296
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(Object) AbstractChannelHandlerContext.java:379
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) AbstractChannelHandlerContext.java:365
    io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(Object) AbstractChannelHandlerContext.java:357
    io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(ChannelHandlerContext, Object) DefaultChannelPipeline.java:1410
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(Object) AbstractChannelHandlerContext.java:379
    io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) AbstractChannelHandlerContext.java:365
    io.netty.channel.DefaultChannelPipeline.fireChannelRead(Object) DefaultChannelPipeline.java:919
    io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read() AbstractNioByteChannel.java:166
    io.netty.channel.nio.NioEventLoop.processSelectedKey(SelectionKey, AbstractNioChannel) NioEventLoop.java:719
    io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized() NioEventLoop.java:655
    io.netty.channel.nio.NioEventLoop.processSelectedKeys() NioEventLoop.java:581
    io.netty.channel.nio.NioEventLoop.run() NioEventLoop.java:493
    io.netty.util.concurrent.SingleThreadEventExecutor$4.run() SingleThreadEventExecutor.java:989
    io.netty.util.internal.ThreadExecutorMap$2.run() ThreadExecutorMap.java:74
    io.netty.util.concurrent.FastThreadLocalRunnable.run() FastThreadLocalRunnable.java:30
    java.lang.Thread.run() Thread.java:834
    ```
   
    Since we are introduced the internal thread pool for handling the client internal executions.
    So the fix is using the internal thread to process the callback of the hasMessageAvailableAsync
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [pulsar] 315157973 commented on pull request #11183: Fix the dead lock when using hasMessageAvailableAsync and readNextAsync

Posted by GitBox <gi...@apache.org>.
315157973 commented on pull request #11183:
URL: https://github.com/apache/pulsar/pull/11183#issuecomment-872699118


   Great work! This is very difficult to troubleshoot, thanks penghui
   When the Future has been completed, use future.whenComplete at this time, and the current thread will be used. A thread switch causes a deadlock. Here is a Demo:
   ```
           CompletableFuture<Void> future = new CompletableFuture<>();
           ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
                   new BasicThreadFactory.Builder().namingPattern("metrics-schedule-pool-%d").build());
           executorService.execute(() -> {
               System.out.println("executorService:" + Thread.currentThread().getName());
               future.complete(null);
           });
           Thread.sleep(5000);
   
           future.whenComplete(((unused, throwable) -> {
               System.out.println("whenComplete:" + Thread.currentThread().getName());
           }));
           System.out.println("out:" + Thread.currentThread().getName());
   ```
   
   ![image](https://user-images.githubusercontent.com/9758905/124219362-d1d04d00-db2e-11eb-8fde-cb25641bd261.png)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [pulsar] gaoran10 commented on a change in pull request #11183: Fix the dead lock when using hasMessageAvailableAsync and readNextAsync

Posted by GitBox <gi...@apache.org>.
gaoran10 commented on a change in pull request #11183:
URL: https://github.com/apache/pulsar/pull/11183#discussion_r662364584



##########
File path: pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ReaderTest.java
##########
@@ -510,4 +514,44 @@ public void testSameSubName() throws Exception {
 
     }
 
+    @Test(timeOut = 30000)
+    public void testAvoidUsingIoThreadToGetValueOfMessage() throws Exception {
+        final String topic = "persistent://my-property/my-ns/testAvoidUsingIoThreadToGetValueOfMessage";
+
+        @Cleanup
+        Producer<Schemas.PersonOne> producer = pulsarClient.newProducer(Schema.AVRO(Schemas.PersonOne.class))
+                .topic(topic)
+                .create();
+
+        producer.send(new Schemas.PersonOne(1));
+
+        @Cleanup
+        Reader<Schemas.PersonOne> reader = pulsarClient.newReader(Schema.AVRO(Schemas.PersonOne.class))
+                .topic(topic)
+                .startMessageId(MessageId.earliest)
+                .create();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        List<Schemas.PersonOne> received = new ArrayList<>(1);
+        // Make sure the message is added to the incoming queue
+        Awaitility.await().untilAsserted(() ->
+                assertTrue(((ReaderImpl<?>) reader).getConsumer().incomingMessages.size() > 0));
+        reader.hasMessageAvailableAsync().whenComplete((has, e) -> {
+            if (e == null && has) {
+                CompletableFuture<Message<Schemas.PersonOne>> future = reader.readNextAsync();
+                // Make sure the future completed
+                Awaitility.await().pollInterval(1, TimeUnit.MILLISECONDS).untilAsserted(future::isDone);

Review comment:
       Why need to wait for the future?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [pulsar] codelipenghui merged pull request #11183: Fix the dead lock when using hasMessageAvailableAsync and readNextAsync

Posted by GitBox <gi...@apache.org>.
codelipenghui merged pull request #11183:
URL: https://github.com/apache/pulsar/pull/11183


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [pulsar] codelipenghui commented on a change in pull request #11183: Fix the dead lock when using hasMessageAvailableAsync and readNextAsync

Posted by GitBox <gi...@apache.org>.
codelipenghui commented on a change in pull request #11183:
URL: https://github.com/apache/pulsar/pull/11183#discussion_r662369792



##########
File path: pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ReaderTest.java
##########
@@ -510,4 +514,44 @@ public void testSameSubName() throws Exception {
 
     }
 
+    @Test(timeOut = 30000)
+    public void testAvoidUsingIoThreadToGetValueOfMessage() throws Exception {
+        final String topic = "persistent://my-property/my-ns/testAvoidUsingIoThreadToGetValueOfMessage";
+
+        @Cleanup
+        Producer<Schemas.PersonOne> producer = pulsarClient.newProducer(Schema.AVRO(Schemas.PersonOne.class))
+                .topic(topic)
+                .create();
+
+        producer.send(new Schemas.PersonOne(1));
+
+        @Cleanup
+        Reader<Schemas.PersonOne> reader = pulsarClient.newReader(Schema.AVRO(Schemas.PersonOne.class))
+                .topic(topic)
+                .startMessageId(MessageId.earliest)
+                .create();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        List<Schemas.PersonOne> received = new ArrayList<>(1);
+        // Make sure the message is added to the incoming queue
+        Awaitility.await().untilAsserted(() ->
+                assertTrue(((ReaderImpl<?>) reader).getConsumer().incomingMessages.size() > 0));
+        reader.hasMessageAvailableAsync().whenComplete((has, e) -> {
+            if (e == null && has) {
+                CompletableFuture<Message<Schemas.PersonOne>> future = reader.readNextAsync();
+                // Make sure the future completed
+                Awaitility.await().pollInterval(1, TimeUnit.MILLISECONDS).untilAsserted(future::isDone);

Review comment:
       This is to simulate the race condition here, the problem happens only the future is already completed, so that will continue using the IO thread to execute the callback.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [pulsar] hangc0276 commented on pull request #11183: Fix the dead lock when using hasMessageAvailableAsync and readNextAsync

Posted by GitBox <gi...@apache.org>.
hangc0276 commented on pull request #11183:
URL: https://github.com/apache/pulsar/pull/11183#issuecomment-873009235


   Great catch! The deadlock it hard to debug.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org