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

PreparedStatement bicommand scheduling on pool are not scheduled on the same connection #590

Closed
BillyYccc opened this issue Apr 13, 2020 · 7 comments
Assignees
Labels
Milestone

Comments

@BillyYccc
Copy link
Member

Version

this should happen in 3.9 version

Context

We meet such exceptions when we use SqlClient#preparedQuery

o.vertx.mysqlclient.MySQLException: Unknown prepared statement handler (1) given to mysqld_stmt_execute
	at io.vertx.mysqlclient.impl.codec.CommandCodec.handleErrorPacketPayload(CommandCodec.java:131)
	at io.vertx.mysqlclient.impl.codec.ExtendedQueryCommandBaseCodec.handleInitPacket(ExtendedQueryCommandBaseCodec.java:28)
	at io.vertx.mysqlclient.impl.codec.QueryCommandBaseCodec.decodePayload(QueryCommandBaseCodec.java:58)
	at io.vertx.mysqlclient.impl.codec.ExtendedQueryCommandCodec.decodePayload(ExtendedQueryCommandCodec.java:100)
	at io.vertx.mysqlclient.impl.codec.MySQLDecoder.decodePacket(MySQLDecoder.java:65)
	at io.vertx.mysqlclient.impl.codec.MySQLDecoder.decode(MySQLDecoder.java:54)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:498)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:437)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	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:748)

One-shot prepared statement executing will schedule a PrepareStatementCommand and ExtendedQueryCommand like this

@Override
public void execute(Tuple arguments, Handler<AsyncResult<R>> handler) {
SqlResultHandler resultHandler = builder.createHandler(handler);
schedule(new PrepareStatementCommand(sql), cr -> {
if (cr.succeeded()) {
PreparedStatement ps = cr.result();
String msg = ps.prepare((TupleInternal) arguments);
if (msg != null) {
handler.handle(Future.failedFuture(msg));
} else {
schedule(builder.createExtendedQuery(ps, arguments, resultHandler), resultHandler);
}
} else {
handler.handle(Future.failedFuture(cr.cause()));
}
});
}

such two commands may be scheduled on the different connections acquired from the pool, we should make sure the BiCommand scheduling happen on the same session

Do you have a reproducer?

see https://groups.google.com/forum/?fromgroups#!topic/vertx/KAdcC_yNG8E
This also happens in https://travis-ci.org/github/eclipse-vertx/vertx-sql-client/jobs/674432633

A simple snippet of code could reproduce this

// set pool size to 2
  @Test
  public void highContendPreparedQuery(TestContext ctx) {
    Async async = ctx.async(15000);
    for (int i = 0; i < 15000; i++) {
      pool.preparedQuery("SELECT " + i).execute(ctx.asyncAssertSuccess(res -> {
        async.countDown();
      }));
    }
    async.await();
  }
@BillyYccc BillyYccc added the bug label Apr 13, 2020
@BillyYccc BillyYccc added this to the 3.9.1 milestone Apr 13, 2020
@vietj
Copy link
Member

vietj commented May 11, 2020

did you work on this issue @BillyYccc ?

@BillyYccc
Copy link
Member Author

BillyYccc commented May 11, 2020 via email

@vietj
Copy link
Member

vietj commented May 11, 2020 via email

@BillyYccc
Copy link
Member Author

BillyYccc commented May 11, 2020 via email

@vietj vietj self-assigned this May 12, 2020
@vietj
Copy link
Member

vietj commented May 12, 2020

I will handle this one @BillyYccc

@vietj
Copy link
Member

vietj commented May 12, 2020

funny there is already such similar test in PgPoolTest

@vietj
Copy link
Member

vietj commented May 12, 2020

the fix is easy, I'm almost done with it. I'll port the test to master as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants