Skip to content

Hang with max pool connections 1 and response_incomplete #1005

Description

@stu-mcd

FYI I no nothing about reactor-netty however I came across the following whilst investigating a Java Azure blob storage read problem

With a PooledConnectionProvider with max connections 1 I am reading from an Azure blob (which internally uses reactor-netty). So the debug output looks like

### READ PART

2020-02-19 10:14:10.298 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX]  onStateChange(GET{uri=YYY, connection=PooledConnection{channel=[id: 0xa380713b, L:/IPADDR:41618 - R:XXX]}}, [disconnecting])
2020-02-19 10:14:10.298 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Releasing channel
2020-02-19 10:14:10.298 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Channel cleaned, now 0 active connections and 1 inactive connections
2020-02-19 10:14:10.299 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Channel acquired, now 1 active connections and 0 inactive connections
2020-02-19 10:14:10.299 DEBUG HttpClientConnect : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Handler is being applied: {uri=YYY, method=GET}
2020-02-19 10:14:10.300 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] onStateChange(GET{uri=YYY, connection=PooledConnection{channel=[id: 0xa380713b, L:/IPADDR:41618 - R:XXX]}}, [request_sent])
2020-02-19 10:14:10.464 DEBUG HttpClientOperations : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Received response (auto-read:false) : [Content-Length=1000, Content-Type=application/octet-stream, Content-Range=bytes 31737266000-31737266999/36285207380, Last-Modified=Sat, 04   Jan 2020 19:29:19 GMT, Accept-Ranges=bytes, ETag="0x8D7914C653EE0E0", Server=Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0, x-ms-request-id=c2a4c0e7-901e- 0089-190d-e78136000000, x-ms-client-request-id=9764e16d-6c0e-4f1a-afbe-d7adbae3e5ce, x-ms-version=2019-02-02, x-ms-meta-earliestExpiry=1609545600, x-ms-  meta-                     orgs=2tQ529v9TgAAlHFk, x-ms-creation-time=Sat, 04 Jan 2020 19:29:19 GMT, x-ms-lease-status=unlocked, x-ms-lease-                state=available, x-ms-blob-               type=BlockBlob, x-ms-server-encrypted=true, Date=Wed, 19 Feb 2020 10:14:09 GMT]
2020-02-19 10:14:10.465 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] onStateChange(GET{uri=YYY, connection=PooledConnection{channel=[id: 0xa380713b, L:/IPADDR:41618 - R:XXX]}},                   [response_received])
2020-02-19 10:14:10.480 DEBUG FluxReceive : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Subscribing inbound receiver [pending: 0, cancelled: false, inboundDone: false]
2020-02-19 10:14:10.480 DEBUG HttpClientOperations : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Received last HTTP packet
### READ PART
2020-02-19 10:14:10.480 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] onStateChange(GET{uri=YYY, connection=PooledConnection{channel=[id: 0xa380713b, L:/IPADDR:41618 - R:XXX]}}, [disconnecting])
2020-02-19 10:14:10.480 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Releasing channel
2020-02-19 10:14:10.480 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Channel cleaned, now 0 active connections and 1 inactive connections
2020-02-19 10:14:10.482 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Channel acquired, now 1 active connections and 0 inactive connections
2020-02-19 10:14:10.482 DEBUG HttpClientConnect : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Handler is being applied: {uri=YYY, method=GET}
2020-02-19 10:14:10.483 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] onStateChange(GET{uri=YYY, connection=PooledConnection{channel=[id: 0xa380713b, L:/IPADDR:41618 - R:XXX]}}, [request_sent])
2020-02-19 10:14:10.566 DEBUG HttpClientOperations : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Received response (auto-read:false) : [Content-Length=1000, Content-Type=application/octet-stream, Content-Range=bytes 31003928000-31003928999/36285207380, Last-Modified=Sat, 04   Jan 2020 19:29:19 GMT, Accept-Ranges=bytes, ETag="0x8D7914C653EE0E0", Server=Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0, x-ms-request-id=c2a4c1c3-901e- 0089-670d-e78136000000, x-ms-client-request-id=ba2df722-04d4-4152-af70-b71d56bb7546, x-ms-version=2019-02-02, x-ms-meta-earliestExpiry=1609545600, x-ms-meta-orgs=2tQ529v9TgAAlHFk, x-ms-creation-time=Sat, 04 Jan 2020 19:29:19 GMT, x-ms-lease-status=unlocked, x-ms-lease-state=available, x-ms-blob-type=BlockBlob, x-ms-server-encrypted=true, Date=Wed, 19 Feb 2020 10:14:09 GMT]
2020-02-19 10:14:10.566 DEBUG PooledConnectionProvider : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] onStateChange(GET{uri=YYY, connection=PooledConnection{channel=[id: 0xa380713b, L:/IPADDR:41618 - R:XXX]}},                   [response_received])
2020-02-19 10:14:10.575 DEBUG FluxReceive : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Subscribing inbound receiver [pending: 0, cancelled: false, inboundDone: false]
2020-02-19 10:14:10.575 DEBUG HttpClientOperations : [id: 0xa380713b, L:/IPADDR:41618 - R:XXX] Received last HTTP packet

Note that my code and the Azure read are all single threaded and blocking.

I then force a tcp reset using iptables e.g.

iptables -A OUTPUT -d 20.38.109.4 -p tcp -j REJECT --reject-with tcp-reset; sleep 1;  iptables -F OUTPUT

This causes AbstractEpollStreamChannel.epollInReady() to catch a "connection reset by peer" and call handleReadException. This is generally handled fine, however if it occurs after the "response_received" it will hang until the Azure timeout for the read triggers.
The logs show "An outbound error could not be processed (Connection closed BEFORE response" (here are the stacks)

  at reactor.netty.channel.ChannelOperations.onError(ChannelOperations.java:208)
  at reactor.core.publisher.Operators.error(Operators.java:182)
  at reactor.core.publisher.MonoError.subscribe(MonoError.java:52)
  at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
  at reactor.netty.NettyOutbound.subscribe(NettyOutbound.java:329)
  at reactor.core.publisher.MonoSource.subscribe(MonoSource.java:58)
  at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
  at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:430)
  at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:503)
  at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.run(PooledConnectionProvider.java:574)
  at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:164)
  at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:-1)
  at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
  at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:388)
  at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
  at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
  at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
  at java.lang.Thread.run(Thread.java:834)

Creation of the AbortedException

at reactor.netty.http.HttpOperations.then(HttpOperations.java:157)
at reactor.netty.NettyOutbound.subscribe(NettyOutbound.java:329)
at reactor.core.publisher.MonoSource.subscribe(MonoSource.java:58)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:430)
at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:503)
at reactor.netty.resources.PooledConnectionProvider$DisposableAcquire.run(PooledConnectionProvider.java:574)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:388)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)

This all causes the PooledConnectionProvider.run() ChannelOperations ops variable to have a channel which is not active. i.e.

ChannelOperations<?, ?> ops = opsFactory.create(pooledConnection, pooledConnection, null);
if (ops != null) {
    ops.bind();
    obs.onStateChange(ops, State.CONFIGURED);
    sink.success(ops);

The ops.channel().isActive() is FALSE, which (though I don't know reactor-netty) seems wrong

Note that though I am using a pool size of 1, the problem seems to be if the pool is full e.g. I have managed to reproduce it using a pool size of 2 (because of the test I can't get Azure to use more than 2 connections).

Steps to Reproduce

Unfortunately since I am using Azure which calls reactor-netty I don't have a way to reproduce without using azure and having access to an Azure blob storage file. Also, it is hard to trigger the reset at the correct time to cause the issue. However I can reproduce this locally in Intellij so I can run any tests or provide any further information as required.

Your Environment

I am using 0.9.4.RELEASE reactor-netty, java 11.0.3 and

io.netty:netty:3.10.6.Final
io.netty:netty-buffer:4.1.44.Final
io.netty:netty-codec:4.1.44.Final
io.netty:netty-codec-http2:4.1.42.Final
io.netty:netty-codec-http:4.1.42.Final
io.netty:netty-codec-socks:4.1.42.Final
io.netty:netty-common:4.1.44.Final
io.netty:netty-handler:4.1.44.Final
io.netty:netty-handler-proxy:4.1.42.Final
io.netty:netty-resolver:4.1.44.Final
io.netty:netty-tcnative-boringssl-static:2.0.26.Final
io.netty:netty-transport:4.1.44.Final
io.netty:netty-transport-native-epoll:4.1.42.Final
io.netty:netty-transport-native-unix-common:4.1.42.Final

I am running on Ubuntu
Linux XXX 4.15.0-64-generic #73-Ubuntu SMP Thu Sep 12 13:16:13 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
netty is using native epoll.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions