vertx-sql-client icon indicating copy to clipboard operation
vertx-sql-client copied to clipboard

query timeout

Open gavinking opened this issue 5 years ago • 26 comments

JDBC and JPA let me set a timeout on a query. Unless I'm missing something, it's a feature that's still needed in the reactive world, and AFAICT there's currently no way to do it in the Vert.x client.

gavinking avatar May 30 '20 16:05 gavinking

what happens when the timeout elapses for the queries in progress ?

vietj avatar May 30 '20 19:05 vietj

what happens when the timeout elapses for the queries in progress ?

They get aborted, and the client receives an exception.

gavinking avatar May 30 '20 19:05 gavinking

Well, it gets aborted, since the timeout only affects one query.

gavinking avatar May 30 '20 19:05 gavinking

The current API I can see in JDBC is Connection#setNetworkTimeout(Executor,int) , is that the API you are referring to or am I missing something ?

vietj avatar May 30 '20 21:05 vietj

Look at Statement.setQueryTimeout().

gavinking avatar May 30 '20 21:05 gavinking

And in JPA it's:

query.setHint("javax.persistence.query.timeout", 2000);

gavinking avatar May 30 '20 21:05 gavinking

Ok that makes more sense to me than what I found before.

we can support this for PG (the API for cancelling a query already exist) but I don't know how other implementations can handle it.

@aguibert @BillyYccc do you know if MySQL, MSSQL and DB2 support this ?

vietj avatar May 30 '20 21:05 vietj

It seems that the actual way to proceed is similar to PostgreSQL which is to open a new connection to the server and execute a KILL QUERY statement using an ID provided by MySQL that we get in MySQLClient at https://github.com/eclipse-vertx/vertx-sql-client/blob/master/vertx-mysql-client/src/main/java/io/vertx/mysqlclient/impl/codec/InitialHandshakeCommandCodec.java#L78

vietj avatar May 30 '20 22:05 vietj

In JDBC the implementation of Statement#setQueryTimeout is opening another connection and send a query cancel signal to ask the server to terminate the executing query. that timeout means query_timeout = network_timeout + statement_timeout

But such mechanisms are not reliable because the cancel request mostly only tells which connection to cancel without any query identifier, so the cancel request might arrive at the server but the query has already been completed which will cause the new query being terminated unexpectedly. And the cancel request might get stuck by the network as well :-)

There is statement-level timeout in most mainstream database servers, which is far more reliable. The timer starts from when the command is handled by the server.

BillyYccc avatar May 31 '20 10:05 BillyYccc

In JDBC the implementation of Statement#setQueryTimeout is opening another connection and send a query cancel signal to ask the server to terminate the executing query.

Ugh, that sounds like a terrible way to do this. But surely that's just the implementation detail of some particular JDBC driver, that can't possibly be the way it's specified to work by JDBC, can it?

gavinking avatar May 31 '20 10:05 gavinking

JDBC does not specify anything.

It is inherent to the client/database protocol design. These protocol (designed in the 80's) are usually not multiplexed.

In practice it means that when the database is executing a query then it will not be able to read anything from the client until the query has finished and the database has sent the response and therefore the only way is to use another connection and send a command to interrupt the query in progress using an identifier (provided by the connection interrupted). This is how it works for PostgreSQL and MySQL at least.

With a multiplexed protocol, you have the opportunity to reuse the same connection to perform this. e.g HTTP/2 do this and provides the ability to reset a stream (request/response).

vietj avatar May 31 '20 10:05 vietj

It is inherent to the client/database protocol design. These protocol (designed in the 80's) are usually not multiplexed.

Right, but I had understood that the databases themselves had ways to specify the query timeout within their proprietary flavors of SQL, that the JDBC drivers were abstracting.

gavinking avatar May 31 '20 11:05 gavinking

It is inherent to the client/database protocol design. These protocol (designed in the 80's) are usually not multiplexed.

Right, but I had understood that the databases themselves had ways to specify the query timeout within their proprietary flavors of SQL, that the JDBC drivers were abstracting.

(Just like in that link I sent you.)

gavinking avatar May 31 '20 11:05 gavinking

I mean, how does it make sense for the client to be the thing responsible for timing out a stuck query?? That's gotta be something the server does.

gavinking avatar May 31 '20 11:05 gavinking

I think there are two ways which are both doable in the client

  1. provide a statementTimeout option which abstracts setting those statement execution timeout for different databases like SET max_execution_time = 5000 for MySQL SET statement_timeout = 5000; for PostgreSQL etc...

the SQL Client executes such queries for every new created connections once the option is configured, if there's a long stuck query the client will get a error signal from the server.

  1. provide a queryTimeout option like JDBC and works in the way like most of the JDBC implementations

this will start a timer for each query and send specific vendor cancel requests if the request exceeds the configured timeout.

In fact the name queryTimeout is ambiguous, it's more like a requestTimeout. I suspect if anyone would use this unreliable mechanism in the high load production environment because nowadays the database server provide a statement execution timeout option, and there's cost setting a timer for every query request.

BillyYccc avatar May 31 '20 12:05 BillyYccc

there's cost setting a timer for every query request.

Well the use case is that you have a default timeout set on the server, but then for a certain long-running query you want to increase the timeout just for that single query.

For example, you never want to wait a minute for a login query. But you might for a query which is rendering a complex report.

So having a single global timeout isn't sufficiently flexible.

gavinking avatar May 31 '20 12:05 gavinking

APIs could be provided like

  1. setting execution timeout in server
    private void configureSessionStatementExecutionTimeoutExample(int timeout, SqlConnection connection) {
        connection.setStatementTimeout(timeout); // execution timeout
    }
  1. cancel by the client
    private void queryWithTimeoutExample(int timeout, SqlClient sqlClient) {
        RequestOptions requestOptions = new RequestOptions();
        requestOptions.setRequestTimeout(timeout);
        sqlClient.query(requestOptions, "SELECT sleep(50)") // long query
                .execute()
                .onFailure(cancelSig -> {
                    // cancelled by the client
                });
    }

In this way the timeout could be configured at the query start and flexible for each timer

BillyYccc avatar May 31 '20 13:05 BillyYccc

@gavinking there might be a configurable settings on the server to timeout a query, that would usually be configured by sysadmin

What we are explaining here is that usually protocols gives the opportunity to cancel a query from the client and the JDBC implementations for the query timeout usually set a timer locally and when the timer fires then JDBC implementations uses this API to cancel the query. This is what the JDBC PostgreSQL and MySQL implementation do.

vietj avatar May 31 '20 13:05 vietj

I think what we can do is:

1/ provide a ability to cancel the current query in progress using a programmatic API like we do have now in PgConnection#cancelRequest

2/ have on the Query instance a timeout that will set a timer and use the connection query cancellation when the timer fires before the query resolves

vietj avatar May 31 '20 13:05 vietj

@BillyYccc can you investigate query cancellation for MS SQL Server ?

vietj avatar May 31 '20 13:05 vietj

In MSSQL ATTENTION message could be sent directly from the current session to the server to cancel the ongoing request, it's something like a out-of-band signal. https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/dc28579f-49b1-4a78-9c5f-63fbda002d2e

BillyYccc avatar May 31 '20 13:05 BillyYccc

@vietj OK, but as I tried to show above, it seems like the right way to do this with MySQL is using a query hint:

select /*+ max_execution_time(1000) */ * from information_schema.tables

https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time

gavinking avatar May 31 '20 13:05 gavinking

yes it might be.

I think however that this requires to rewrite and understand the user SQL, which is practically something client implementations try to avoid because it is opening a can of worms.

On 31 May 2020, at 15:46, Gavin King [email protected] wrote:

@vietj https://github.com/vietj OK, but as I tried to show above, it seems like the right way to do this with MySQL is using a query hint:

select /*+ max_execution_time(1000) */ * from information_schema.tables — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/eclipse-vertx/vertx-sql-client/issues/668#issuecomment-636473670, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABXDCRKNZFBHMLSCH4BAFDRUJNTBANCNFSM4NOYCZ7Q.

vietj avatar May 31 '20 14:05 vietj

I think however that this requires to rewrite and understand the user SQL

Well, I'm pretty sure this particular query hint gets inserted right after the very first non-comment token in the query (i.e. after select, update, delete, replace, or insert)... so really not very difficult.

Anyway, it's documented here.

which is practically something client implementations try to avoid because it is opening a can of worms.

I don't think that's right. Every JDBC and ODBC driver does significant rewriting of the user-written SQL, to process parameters, and to process the escape syntax.

I don't really understand why you guys think that the Vert.x client should be so different to other database drivers in this respect.

gavinking avatar May 31 '20 14:05 gavinking

On 31 May 2020, at 16:19, Gavin King [email protected] wrote:

I think however that this requires to rewrite and understand the user SQL

Well, I'm pretty sure this particular query hint gets inserted right after the very first non-comment token in the query (i.e. after select, update, delete, replace, or insert)... so really not very difficult.

Anyway, it's documented here https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-overview.

which is practically something client implementations try to avoid because it is opening a can of worms.

I don't think that's right. Every JDBC and ODBC driver does significant rewriting of the user-written SQL, to process parameters, and to process the escape syntax.

maybe, I haven't checked, however that is something I want to avoid in SQL client

I don't really understand why you guys think that the Vert.x client should be so different to other database drivers in this respect.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/eclipse-vertx/vertx-sql-client/issues/668#issuecomment-636477643, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABXDCX5KZB74IOMBPSMI2DRUJRPTANCNFSM4NOYCZ7Q.

vietj avatar May 31 '20 14:05 vietj