Statement.setQueryTimeout doesn't work on Oracle 18c jdbc driver
Asked Answered
C

3

6

We have a J2EE application on a payara 5.2020 server that executes a long running query (PL/SQL that executes for a couple of hours). To avoid a timeout exception, we use this sentence at StatementLevel:

statement.setQueryTimeout(0);

This works using Oracle jdbc drivers version 12c, but when we have migrated to Oracle 18c, and we changed the driver to the version 18c, the query execution stops after 15 minutes with this exception. The code works with Oracle 12 and Oracle 18 is the change in the driver's jar what brings up the problem. The problem has been reproduced in Linux and Windows machines:

2021-06-14T07:50:01.762+0200|SEVERE: java.sql.SQLRecoverableException: Error de E/S: Socket read interrupted
    at oracle.jdbc.driver.T4CCallableStatement.executeForRows(T4CCallableStatement.java:946)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1136)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3640)
    at oracle.jdbc.driver.T4CCallableStatement.executeInternal(T4CCallableStatement.java:1318)
    at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3752)
    at oracle.jdbc.driver.OracleCallableStatement.execute(OracleCallableStatement.java:4242)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1079)
    at com.sun.gjc.spi.base.PreparedStatementWrapper.execute(PreparedStatementWrapper.java:532)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at com.sun.gjc.spi.jdbc40.ProfiledConnectionWrapper40$1.invoke(ProfiledConnectionWrapper40.java:437)
    at com.sun.proxy.$Proxy324.execute(Unknown Source)
    at org.apache.jsp.index_jsp.callPL(index_jsp.java:49)
    at org.apache.jsp.index_jsp._jspService(index_jsp.java:108)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:111)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:411)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:473)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:377)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1636)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:259)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:757)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:577)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.io.InterruptedIOException: Socket read interrupted
    at oracle.net.nt.TimeoutSocketChannel.handleInterrupt(TimeoutSocketChannel.java:262)
    at oracle.net.nt.TimeoutSocketChannel.read(TimeoutSocketChannel.java:184)
    at oracle.net.ns.NSProtocolNIO.doSocketRead(NSProtocolNIO.java:544)
    at oracle.net.ns.NIOPacket.readHeader(NIOPacket.java:234)
    at oracle.net.ns.NIOPacket.readPacketFromSocketChannel(NIOPacket.java:174)
    at oracle.net.ns.NIOPacket.readFromSocketChannel(NIOPacket.java:122)
    at oracle.net.ns.NIOPacket.readFromSocketChannel(NIOPacket.java:100)
    at oracle.net.ns.NIONSDataChannel.readDataFromSocketChannel(NIONSDataChannel.java:86)
    at oracle.jdbc.driver.T4CMAREngineNIO.prepareForUnmarshall(T4CMAREngineNIO.java:762)
    at oracle.jdbc.driver.T4CMAREngineNIO.unmarshalUB1(T4CMAREngineNIO.java:427)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:394)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:255)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:610)
    at oracle.jdbc.driver.T4CCallableStatement.doOall8(T4CCallableStatement.java:249)
    at oracle.jdbc.driver.T4CCallableStatement.doOall8(T4CCallableStatement.java:82)
    at oracle.jdbc.driver.T4CCallableStatement.executeForRows(T4CCallableStatement.java:924)

It seems the transport layer has been migrated to java.nio and the method setQueryTimeout is no longer

Things We've tried:

  1. Setting the default Statement timeout to -1 in the JDBC Connection Pool Advanced Attributes screen in payara console.
  2. Trying to set the time directly in the connection with conn.setNetworkTimeout(Executors.newFixedThreadPool(1), 0) didn't make any effect.
  3. In different sources we have found that these properties below should affect the network timeout evaluation. We set them as JVM properties for payara startup (-Doracle.net.CONNECT_TIMEOUT=xxx) and as JDBC Connection Pool Properties, both cases with values 0 and -1. Didn't work in any case.
  • oracle.net.CONNECT_TIMEOUT
  • oracle.net.READ_TIMEOUT
  • oracle.jdbc.ReadTimeout

Sources:

enter image description here

4.- As we are accessing the DataSource through payara DataSource pool, we cannot cast the com.sun.gjc.spi.jdbc40.DataSource40 (class provided by payara) to an OracleDataSource, but we created the DataSorce programatically to set the connection properties as shown here and setting the properties seen in the image above but it doesn't work:

public static Properties oracleProperties() {
    // Already tried -1 and 0
    Properties properties = new Properties();
    properties.put("Oracle.net.CONNECT_TIMEOUT", 0);
    properties.put("Oracle.net.READ_TIMEOUT", 0);
    properties.put("Oracle.jdbc.ReadTimeout", 0);
    return properties;
}

public static OracleDataSource createDataSource() throws Exception {
    OracleDataSource ods = new OracleDataSource();
    ods.setURL("jdbc:oracle:thin:@itauc4602x:1521/BDExp");
    ods.setUser("enevac");
    ods.setPassword("enevac");
    ods.setDataSourceName("OracleXADataSource");
    ods.setLoginTimeout(0);
    // default connection properties to avoid timeoutException
    ods.setConnectionProperties(oracleProperties());
    return ods;
}
  1. Has anyone faced this problem, any idea on how to avoid the timeOut restriction?
  2. Why 15 minutes?, according to the reference, the default value for oracle.net.ReadTimeout is 10 minutes.

Update: To explain in more detail why I think the problem is in the driver and why I discard other possible origins of the exception, I assume the timeout can be raised from three sources:

  • Network timeout: I discard it cause I'm testing a payara server in my local machine against the development database, with no firewall in between.
  • Database server: the DBA has checked the Oracle net services configuration and there's no limit set that explains the 15 minutes cut. Besides, in these case, an SQLException would be expected with some kind of ORA-xxx error code.
  • JDBC: this can be set at connection level, statement level and transaction level. As I said at the beginning, the code works with oracle 12c drivers against Oracle 12 and Oracle 18 servers, it was the change of the driver's jar what make the code stop working.
Churchwoman answered 14/6, 2021 at 14:59 Comment(7)
Are you sure the timeout is coming from Oracle or JDBC/Java; and isn't something in the network layer killing what it thinks is an idle connection? If it is Oracle have you looked at DB resource limits or SQL*Net settings on the server end?Linsang
I'm doing the test in my local server, and the exception is thrown in the server not in the Oracle server. If the exception were thrown by the Oracle server, there should be and SQLException with some ORA-xxx error code.Churchwoman
There are multiple levels of query timeouts, configured per statement, session and server hard limit. The smallest value wins: if you set the query timeout to 0 sec but the session limit is 60 sec, than the query will be interrupted after 60 sec. Check your database to see what are defaults for the connection.Crocoite
I'm talking about what killed it, not what threw the exception. JDBC is seeing the terminated connection - that doesn't tell you which layer terminated it. Socket read interrupted suggests something killed it at the network level; but that could be the server SQL*Net settings I think (in listener.ora or sqlnet.ora).Linsang
@AlexPoole thank you for the tips, I've expanded the question to clarify why I think the problema is in the driver. Basically, I have a test application with the pool configured against the ORacle 18c server. If I use the 12c driver it works, If I change the driver jar to oracle 18c driver, It stops working.Churchwoman
Are you able to create minimal reproducible standalone application and run strace on it? This Operation interrupted is something returned from OS to JVM and it is to JDBC driver to handle it. It can possibly also be JDBC bug.Flyer
For example: 12.2.0.1 and Above JDBC Connections Sometimes Fail With: IO Error: Socket Read Interrupted (Doc ID 2612009.1): The 12.2.0.1 and above JDBC driver uses Java NIO calls in blocking mode, which can be impacted by any interrupt() calls being made by the application. This differs from previous versions of the JDBC driver, which used stream-based I/O API calls that were not affected by calls to interrupt(). `Inspect the application code for any interrupt() method calls. Alternatively, set the following connection property: oracle.jdbc.javaNetNio=false.Flyer
C
6

Finally the problem was fixed configuring in the payara pool the "connectionProperties" custom property of the OracleDataSource. As @ibre5041 pointed, setting the property oracle.jdbc.javaNetNio=false changes the transport layer used by the driver and it starts working as the oracle 12c previous version.

According to Oracle reference, the OracleDataSource implementors can receive the connection properties as a java.util.Properties object.

Table 8-2 Oracle Extended Data Source Properties

  • Name: connectionProperties
  • Type: java.util.Properties
  • Description: Specifies the connection properties.

To set a multivalued property to the jdbc pool in the Payara Admin Console, you have to set the properties as (prop1=value1,prop2=value2), (Thank you again Ondro Mihályi). So in our case we set:

connectionProperties = (oracle.jdbc.ReadTimeout=0, oracle.jdbc.javaNetNio=false)

As a summary of what works and doesn't using Oracle 18c jdbc driver (every step tested separately)::

  • Setting timeout at statement level doesn't work:

    statement.setQueryTimeout(0)

  • Setting timeout at connection level, with -1 or 0, doesn't work:

    conn.setNetworkTimeout(Executors.newFixedThreadPool(1), timeout in ms)

  • Setting timeout properties programmatically in the java.util.connection using the OracleDataSource makes it work as indicated in the question.

  • Setting timeout properties as JVM properties makes it work if the limit is below 15min, but if you set a value > 15 minutes the exceptions is thrown, so setting to 0 or -1 has no effect:

So this makes the query stop after 10 secs: -Doracle.net.CONNECT_TIMEOUT=10000 -Doracle.net.READ_TIMEOUT=10000 -Doracle.jdbc.ReadTimeout=10000

But with this stops after 15 minutes: -Doracle.net.CONNECT_TIMEOUT=-1 -Doracle.net.READ_TIMEOUT=-1 -Doracle.jdbc.ReadTimeout=-1

  • Setting oracle.net.keepAlive=true as JVM property as @Nirmala suggested doesn’t work.
  • Setting oracle.jdbc.javaNetNio=false as JVM property as @ibre5041 makes it work. So it points to some problem with the java.nio transport layer.

Anyway, we opened a support issue to Oracle, cause the jdbc api statement.setQueryTimeout(0) should work without having to configure the datasource, I'll put the response when the case is closed.

Churchwoman answered 23/6, 2021 at 4:53 Comment(3)
Had the same issue, helped me a ton, thanks for the detail!Moyna
Is it still broken? Have you ever tried with Oracle 19 and 21?Counterattack
Sorry, haven't tried yet.Churchwoman
Q
3

According Doc ID 2612009.1(https://support.oracle.com/knowledge/Middleware/2612009_1.html): All Oracle JDBC drivers starting with 12.2.0.1 have a bug, and also have a workaround for it: You are need to set property -Doracle.jdbc.javaNetNio=false when starting your application.

Quadragesima answered 28/3, 2023 at 7:55 Comment(0)
B
0

The query execution could be stopped because of default tcp connection timeout. Can you set keepalive property "oracle.net.keepAlive" to “true” and verify?

Blaze answered 14/6, 2021 at 19:4 Comment(2)
You mean to set it as JVM property or connection property?Churchwoman
This is JVM property. But it does not work by its own. You will also need to to TCP Keepalive interval (time) on Linux kernel level. By default this interval is 2 hours. tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.htmlFlyer

© 2022 - 2024 — McMap. All rights reserved.