Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force driver to use Vert.x event loop #4

Closed
wants to merge 15 commits into from

Conversation

Sammers21
Copy link
Contributor

@Sammers21 Sammers21 commented Aug 17, 2018

Fixes #3

@Sammers21 Sammers21 added the enhancement New feature or request label Aug 17, 2018
@Sammers21 Sammers21 self-assigned this Aug 17, 2018
@Sammers21 Sammers21 requested review from vietj and tsegismont August 17, 2018 07:45
Sammers21 added a commit that referenced this pull request Aug 17, 2018
@Sammers21
Copy link
Contributor Author

@vietj , @tsegismont , can you review?

@vietj
Copy link
Contributor

vietj commented Aug 17, 2018

so if we use this event loop, what are we doing a on cassandra event loop callback ? can you point out code ?

@Sammers21
Copy link
Contributor Author

Sammers21 commented Aug 17, 2018

You mean in which thread we are, when a callback is called?

@Sammers21
Copy link
Contributor Author

@vietj
Copy link
Contributor

vietj commented Aug 17, 2018

I was actually referring to the code called back from Cassandra like:

vertxExecuteFuture.setHandler(executionResult -> {
   // vertx code
});

in the final callback to the user code:

  • do we check that the event loop thread is the same
  • the vertx context is the same as well

do you use ContextInternal#executeFromIO before calling user code ?

@vietj
Copy link
Contributor

vietj commented Aug 17, 2018

@Sammers21
Copy link
Contributor Author

Ah, I got it. Sure, we need to call ContextInternal#executeFromIO before invoking application code

@Sammers21
Copy link
Contributor Author

I also think that driver should use acceptor event loop because we can't specify one for networking and one for calling handlers

@Sammers21
Copy link
Contributor Author

@vietj , can you review, please?

@Sammers21
Copy link
Contributor Author

@vietj , ping

@vietj
Copy link
Contributor

vietj commented Sep 5, 2018

I think we should have tests with callbacks that:

1/ check the context is the right one
2/ throwing an exception in the handler is actually reported on the Context#exceptionHandler

@Sammers21
Copy link
Contributor Author

@vietj , done. Can you review one more time, please?

} else {
if (resultHandler != null) {
resultHandler.handle(Future.failedFuture(executionResult.cause()));
vertxExecuteFuture.setHandler(executionResult ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can simplify to :

    vertxExecuteFuture.setHandler(executionResult -> {
      if (resultHandler != null) {
        ctx.executeFromIO(executionResult, resultHandler);
      }
    });

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@vietj
Copy link
Contributor

vietj commented Sep 20, 2018

there are simplifications you can do in callbacks, there is a method on ContextInternal that allows to run executeFromIO using an Handler<T> that takes a T as parameter you can use. Also re-creating a Future from a Future is not necessary. Take a look.

@Sammers21
Copy link
Contributor Author

@vietj, can you review once again?

@Sammers21
Copy link
Contributor Author

@tsegismont , can you review, please?

Copy link
Contributor

@tsegismont tsegismont left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sammers21 the changes look good to me, but it would be better to create a dedicated test for context checking.

@@ -50,10 +50,12 @@ private void connectAndDisconnect(TestContext context, CassandraClientOptions op
);
Async async = context.async(2);
cassandraClient.connect("system", connectEvent -> {
checkContext(context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it is necessary to do this in all the tests in the test suite. Instead, we could have a test dedicated to context checking (with different use cases for shared/non shared client)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it is necessary to do this in all the tests in the test suite. Instead, we could have a test dedicated to context checking

Agree.

with different use cases for shared/non shared client

Why do we need to have context checks for both of them?

@Sammers21
Copy link
Contributor Author

Sammers21 commented Oct 5, 2018

It seems that there is something wrong with this approach. We are getting IllegalStateExceptions:

java.lang.IllegalStateException: Uh oh! Event loop context executing with wrong thread! Expected null got Thread[main,5,main]
	at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:318)
	at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:188)
	at io.vertx.core.impl.EventLoopContext.executeFromIO(EventLoopContext.java:23)
	at io.vertx.cassandra.impl.CassandraRowStreamImpl.tryToTriggerEndOfTheStream(CassandraRowStreamImpl.java:134)
	at io.vertx.cassandra.impl.CassandraRowStreamImpl.endHandler(CassandraRowStreamImpl.java:82)
	at io.vertx.cassandra.StreamingTest.lambda$emptyStream$11(StreamingTest.java:95)
java.lang.IllegalStateException: Uh oh! Event loop context executing with wrong thread! Expected null got Thread[main,5,main]
	at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:318)
	at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:188)
	at io.vertx.core.impl.EventLoopContext.executeFromIO(EventLoopContext.java:23)
	at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:179)
	at io.vertx.core.impl.EventLoopContext.executeFromIO(EventLoopContext.java:23)
	at io.vertx.cassandra.impl.CassandraClientImpl.lambda$null$15(CassandraClientImpl.java:253)
	at io.vertx.core.impl.FutureImpl.setHandler(FutureImpl.java:79)
	at io.vertx.cassandra.impl.CassandraClientImpl.lambda$disconnect$16(CassandraClientImpl.java:253)
	at io.vertx.cassandra.impl.CassandraClientImpl.executeWithSession(CassandraClientImpl.java:262)
	at io.vertx.cassandra.impl.CassandraClientImpl.disconnect(CassandraClientImpl.java:251)
	at io.vertx.cassandra.ExecutionTest.lambda$simpleReleaseVersionSelect$8(ExecutionTest.java:124)

https://travis-ci.org/vert-x3/vertx-cassandra-client/jobs/437563597

@Sammers21
Copy link
Contributor Author

@vietj , @tsegismont do you have any ideas on what is wrong?

@Sammers21
Copy link
Contributor Author

Sammers21 commented Oct 5, 2018

I am also not able to reproduce it on my on my laptop (tried 10 times). looks weird.

@tsegismont
Copy link
Contributor

@Sammers21 looking

@tsegismont
Copy link
Contributor

I can reproduce on my box with a vertx-unit repeat rule. Working on the fix.

@tsegismont
Copy link
Contributor

@Sammers21 the issue is here:

sessionFuture.setHandler(executionResult -> {
if (connectHandler != null) {
context.executeFromIO(executionResult, connectHandler);
}
});

When sessionFuture.setHandler is invoked, it is possible that the future is already completed. Then the calling thread will invoke the handler. In our case, the calling thread is the Java main thread.

The issue is not specific to this PR, it is simply brought forward because executeFromIO does thread checks.

@tsegismont
Copy link
Contributor

@Sammers21 I believe the acceptor event loop group shouldn't be used

@Override
public EventLoopGroup eventLoopGroup(ThreadFactory threadFactory) {
return vertx.getAcceptorEventLoopGroup();
}

It's a single thread group and we don't all Datastax connections to handled by a single event loop

@tsegismont
Copy link
Contributor

@Sammers21 I overlooked key changes the first time I reviewed this PR. For this to work we must be sure that the Datastax driver callbacks will always be invoked on the connection event loop. I don't believe this is true.

@vietj
Copy link
Contributor

vietj commented Oct 8, 2018 via email

@tsegismont
Copy link
Contributor

@vietj right. But my point was that chances are low that the event loop running the driver connection is the same as the event loop running the calling context.

In the end, to make this right, we'll add a lot of complexity for a small benefit.

@Sammers21 @vietj I just submitted #9 and I would rather close this one

@Sammers21
Copy link
Contributor Author

@tsegismont, the #9 PR is fine to me. But, can we make the problem more explicit? A Vert.x instance maintain the acceptor event loop, which is a single-threaded event loop. The event loop should be used for network operations and never be used for executing user code. Right?

Why can't we use the acceptor event loop for network operations with Cassandra and delegate "calling handlers" to a context related event loop, so user code(inside one verticle) will always be executed in the same context.

@tsegismont
Copy link
Contributor

tsegismont commented Oct 9, 2018

A Vert.x instance maintain the acceptor event loop, which is a single-threaded event loop.

The acceptorEventLoopGroup is a group of a single event loop:

https://github.com/eclipse-vertx/vert.x/blob/9cafc3673424722c463d3e9d3754821f1bb86484/src/main/java/io/vertx/core/impl/VertxImpl.java#L153-L155

Its purpose is to avoid having connections (HTTP servers, net servers) rejected because all the event loops used for verticles are too busy. It should not be reused in the Cassandra client to create event loop for driver connections.

@Override
public EventLoopGroup eventLoopGroup(ThreadFactory threadFactory) {
return vertx.getAcceptorEventLoopGroup();
}

So the code above is wrong. If anything, we should use VertxInternal#getEventLoopGroup

The event loop should be used for network operations and never be used for executing user code. Right?

Not sure what you mean.

Why can't we use the acceptor event loop for network operations with Cassandra and delegate "calling handlers" to a context related event loop, so user code(inside one verticle) will always be executed in the same context.

I believe I answered the question related to acceptor. As for using Vert.x event loops, it sounded to me like a good idea in the beginning, but taking a closer look I can only see problems but not much benefits.

The problems are:

  • drivers connections are handled by a single event loop
  • there is no guarantee that a verticle using our client will always be assigned the same driver connection

Therefore, the benefits (a bit less threads) are not going to outweigh the added complexity.

@tsegismont
Copy link
Contributor

@Sammers21 I'll go ahead and merge #9

We can either close #3, or keep it open and try to demonstrate the benefits with a benchmark?

@Sammers21
Copy link
Contributor Author

@tsegismont , I we can close this PR, and keep the #3 issue open.

@Sammers21
Copy link
Contributor Author

try to demonstrate the benefits with a benchmark

@tsegismont, yes I think we need to measure performance benefits

@Sammers21 Sammers21 closed this Oct 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

Successfully merging this pull request may close these issues.

3 participants