-
Type:
Bug
-
Resolution: Gone away
-
Priority:
Major - P3
-
None
-
Affects Version/s: 5.2.0
-
Component/s: Connection Management
-
None
-
Java Drivers
-
None
-
None
-
None
-
None
-
None
-
None
Summary
With timeoutMS set and the Netty transport, a write on a pooled connection that the far side closed while idle fails with a non-retryable MongoInternalException instead of a retryable MongoSocketException. Reported in HELP-102596 (reactive-streams 5.6.5). Reproduced on 5.6.5 and 5.14.0; NettyStream is unchanged on main.
com.mongodb.MongoInternalException: Unexpected exception Caused by: java.util.NoSuchElementException: ChannelInboundHandlerAdapter at io.netty.channel.DefaultChannelPipeline.addBefore\(DefaultChannelPipeline.java:246\) at com.mongodb.internal.connection.netty.NettyStream.addWriteTimeoutHandler\(NettyStream.java:270\) at com.mongodb.internal.connection.netty.NettyStream.writeAsync\(NettyStream.java:256\) at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendMessageAsync$12\(InternalStreamConnection.java:687\)
Cause
- Server closes an idle pooled connection. Netty's DefaultChannelPipeline destroys the pipeline on unregister.
- NettyStream only records the close for pending readers; it does not mark itself closed, so the pool hands the dead connection to the next operation.
- NettyStream.addWriteTimeoutHandler calls pipeline().addBefore("ChannelInboundHandlerAdapter", ...) with no guard. It runs only when TimeoutContext.getWriteTimeoutMS() != 0, i.e. when timeoutMS is set.
- NoSuchElementException is neither MongoException nor IOException, so throwTranslatedWriteException wraps it in MongoInternalException, which isRetryableException rejects.
- Without timeoutMS the same write fails with ClosedChannelException -> MongoSocketWriteException and the operation is retried transparently. CSOT therefore turns a routine retryable connection-closed error into a hard failure.
Relation to JAVA-6302
JAVA-6302 makes getWriteTimeoutMS() fall back to the socket timeout when timeoutMS is unset. That installs the WriteTimeoutHandler on every Netty write, so this bug would then affect every user with socketTimeoutMS set. Fix this before or together with JAVA-6302.
Proposed fix
In NettyStream.writeAsync: if the stream is closed or the channel is inactive, fail the handler with MongoSocketClosedException; map NoSuchElementException from addBefore to MongoSocketWriteException. Both keep the error retryable. Also consider marking the stream closed from the channel closeFuture listener so the pool prunes the connection.
Reproduction
Netty transport, maxPoolSize=1, timeoutMS set, replica set. Insert once, close that connection at a TCP proxy without touching monitor connections, insert again.
- related to
-
JAVA-6302 Server monitor handshake write has no effective timeout — MongoClient never recovers after frozen-flow TCP disruption (async transports)
-
- Investigating
-