[JAVA-2631] AsynchronousSocketChannelStream open methods don't always wrap IOException in MongoSocketOpenException Created: 10/Oct/17  Updated: 29/Oct/23  Resolved: 09/Jan/18

Status: Closed
Project: Java Driver
Component/s: Connection Management
Affects Version/s: 3.0.0
Fix Version/s: 3.6.2

Type: Bug Priority: Minor - P4
Reporter: Farès Hassak Assignee: Jeffrey Yemin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: PNG File failedTestAsync.png     PNG File passedTestAsync.png    

 Description   

Hello,

The test below is failed :

AsyncStreamTimeoutsSpecification should throw a MongoSocketOpenException when the AsynchronousSocket Stream fails to open

Expected exception of type 'com.mongodb.MongoSocketOpenException', but got 'com.mongodb.MongoException'
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:85)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:72)
	at com.mongodb.connection.AsyncStreamTimeoutsSpecification.should throw a MongoSocketOpenException when the AsynchronousSocket Stream fails to open(AsyncStreamTimeoutsSpecification.groovy:57)
Caused by: com.mongodb.MongoException: java.io.IOException: Le délai de temporisation de sémaphore a expiré.
 
	at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java:136)
	at com.mongodb.connection.AsyncStreamTimeoutsSpecification.should throw a MongoSocketOpenException when the AsynchronousSocket Stream fails to open(AsyncStreamTimeoutsSpecification.groovy:54)
Caused by: java.io.IOException: Le délai de temporisation de sémaphore a expiré.
 
	at sun.nio.ch.Iocp.translateErrorToIOException(Iocp.java:309)
	at sun.nio.ch.Iocp.access$700(Iocp.java:46)
	at sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:399)
	at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:748)

InternalStreamConnection.open() catch an exception of type java.io.IOException (Throwable t)
and transform it to MongoException.
But test expect a MongoSocketOpenException.
And MongoException is not an instance of MongoSocketOpenException.
(the opposite is true).
So the test is failed.

 
class InternalStreamConnection
 
  public void open() {
        isTrue("Open already called", stream == null);
        stream = streamFactory.create(serverId.getAddress());
        try {
            stream.open();
            description = connectionInitializer.initialize(this);
            opened.set(true);
            sendCompressor = findSendCompressor(description);
            LOGGER.info(format("Opened connection [%s] to %s", getId(), serverId.getAddress()));
        } catch (Throwable t) {
            close();
            if (t instanceof MongoException) {
                throw (MongoException) t;
            } else {
                throw new MongoException(t.toString(), t);
            }
        }
    }

A possible solution :

The IOException is throw by stream.open();
So, AsynchronousSocketChannelStream.open() must catch the IOException and transform it to MongoSocketOpenException
(like SocketChannelStream.open() do ):

 
  @Override
    public void open() throws IOException {
       try {
           FutureAsyncCompletionHandler<Void> handler = new FutureAsyncCompletionHandler<Void>();
           openAsync(handler);
           handler.getOpen();
       } catch (IOException e) {
           close();
           throw new MongoSocketOpenException("Exception opening socket", getAddress(), e);
       }
    }

Farès



 Comments   
Comment by Githook User [ 09/Jan/18 ]

Author:

{'name': 'Jeff Yemin', 'username': 'jyemin', 'email': 'jeff.yemin@10gen.com'}

Message: JAVA-2631: In AsynchronousSocketChannelStream.OpenCompletionHandler, wrap any IOException with MongoSocketOpenException rather than just ConnectException
Branch: 3.6.x
https://github.com/mongodb/mongo-java-driver/commit/e0368f1d0ff82c679796a735fbdfe3be5488ebb8

Comment by Githook User [ 09/Jan/18 ]

Author:

{'name': 'Jeff Yemin', 'username': 'jyemin', 'email': 'jeff.yemin@10gen.com'}

Message: JAVA-2631: In AsynchronousSocketChannelStream.OpenCompletionHandler, wrap any IOException with MongoSocketOpenException rather than just ConnectException
Branch: master
https://github.com/mongodb/mongo-java-driver/commit/f9dd53da716c4042b3b62cb9d7a753ca5fa95a58

Comment by Jeffrey Yemin [ 08/Jan/18 ]

https://github.com/mongodb/mongo-java-driver/pull/439

Comment by Charles Sarrazin (Inactive) [ 08/Jan/18 ]

I also agree. The issue should be handled at a lower level than in

AsynchronousSocketChannelStream::open

method.

Comment by Jeffrey Yemin [ 08/Jan/18 ]

The solution in https://github.com/mongodb/mongo-java-driver/pull/421 is also viable, but I think the above conveys the intent a bit better by handling it a lower level.

Comment by Jeffrey Yemin [ 08/Jan/18 ]

OK, I think I see the problem now, and a possible fix would be to change this line from:

if (exc instanceof ConnectException)

to

if (exc instanceof SocketException)

Comment by Charles Sarrazin (Inactive) [ 08/Jan/18 ]

From what I understand, this is a low-level error from Windows-based environments.
From what I could see, the error seems to mean that the port is closed https://stackoverflow.com/a/34751446/1806725

I also checked that the Java source code also seems to be using the ConnectEx behind the scenes (https://github.com/dmlloyd/openjdk/blob/342a565a2da8abd69c4ab85e285bb5f03b48b2c9/src/java.base/windows/native/libnio/ch/WindowsAsynchronousSocketChannelImpl.c), so this may actually be linked to the actual Java socket implementation on Windows.

That explains the error message : "Le délai de temporisation de sémaphore a expiré.".

On OSX (10.13.2 High Sierra), I also have an error, but the stack trace seems to be different:

Expected exception of type 'com.mongodb.MongoSocketOpenException', but got 'com.mongodb.MongoException'
	at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:85)
	at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:72)
	at com.mongodb.connection.AsyncStreamTimeoutsSpecification.should throw a MongoSocketOpenException when the AsynchronousSocket Stream fails to open(AsyncStreamTimeoutsSpecification.groovy:57)
Caused by: com.mongodb.MongoException: java.net.SocketException: Network is unreachable
	at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java:136)
	at com.mongodb.connection.AsyncStreamTimeoutsSpecification.should throw a MongoSocketOpenException when the AsynchronousSocket Stream fails to open(AsyncStreamTimeoutsSpecification.groovy:54)
Caused by: java.net.SocketException: Network is unreachable
	at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishConnect(UnixAsynchronousSocketChannelImpl.java:252)
	at sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:198)
	at sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
	at sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:301)
	at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:748)

In any case, from my perspective, I think the test expects the wrong exception, or that we incorrectly wrap the exception in some cases.

Comment by Farès Hassak [ 21/Dec/17 ]

Hi,

I still have the same error with the last commit of the master branch.
I will try with another pc/os.

Farès

Comment by Ross Lawley [ 19/Dec/17 ]

Hi Farès Hassak, we've still been unable to replicate this, I'd be interested if this is an issue you can still replicate.

Regards,

Ross

Comment by Farès Hassak [ 13/Oct/17 ]

I have updated java and still have same error.
More details about my environnement
------------------------------------------------------------
Gradle 4.0.1
------------------------------------------------------------

Build time: 2017-07-07 14:02:41 UTC
Revision: 38e5dc0f772daecca1d2681885d3d85414eb6826

Groovy: 2.4.11
Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM: 1.8.0_144 (Oracle Corporation 25.144-b01)
OS: Windows 10 10.0 amd64

mongod -version
db version v3.4.7
git version: cf38c1b8a0a8dca4a11737581beafef4fe120bcd
OpenSSL version: OpenSSL 1.0.1u-fips 22 Sep 2016
allocator: tcmalloc
modules: none
build environment:
distmod: 2008plus-ssl
distarch: x86_64
target_arch: x86_64

Comment by Farès Hassak [ 11/Oct/17 ]

D:\github\mongo-java-driver (JAVA-2631)
λ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

Comment by Ross Lawley [ 11/Oct/17 ]

Hi Farès Hassak,

Thank you for the ticket, we're unable to replicate here. The `AsynchronousSocketChannelStream#openAsync` method is responsible for catching the `IOException` and wrapping it in a `MongoSocketOpenException`.

I'm interested why that is not happening in your case? To see if we can replicate, could you let me know what version of Java you are using?

Ross

Comment by Farès Hassak [ 10/Oct/17 ]

https://github.com/mongodb/mongo-java-driver/pull/421

Generated at Thu Feb 08 08:57:42 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.