Oracle JDBC ReadTimeout QueryTimeout

If a query exceeds the oracle.jdbc.ReadTimeout without receiving any data, an exception is thrown and the connection is terminated by the Oracle driver on the client. Unfortunately the session will still be queued on the database and continue to wait for locks, hold any current locks, and complete any DML/PL*SQL procedures that are pending on the server-side of the orphaned connection.

If an application, on a another connection, due to ReadTimeout exception, retries DML/PL*SQL  which requires locks, those queries will queue behind the initial DML/PL*SQL. This will increase the workload exacerbating the situation. On applications with retries, this can be observed by querying the v$session table or gv$session on RAC and noting new sessions started periodically based on the ReadTimeout interval. The sessions may often have the same SQL_ID and/or SQL_HASH_VALUE.

If stmt.setQueryTimeout(Seconds) is issued and the statement exceeds the timeout, it will attempt to cancel the associated in-progress or queued DML/PL*SQL. Assuming the connection is still alive, it will not leave locks and pending or in-progress DML/PL*SQL procedures on the database. One potential down side with QueryTimeout is that if data is trickling in from the server, it won't flexibly extend the timeout like ReadTimeout.

Whenever a ReadTimeout occurs, an attempt should be made to cleanup the orphaned server-side session to eliminate locks, and pending and in-progress DML/PL*SQL. Of course this would have to occur out-of-band on another connection. You will need a connection ID.

When connections are abruptly terminated (no TCP fin/ack), they will continue to be queued, execute, hold locks, and block other queries until detected and terminated which by default is ~2 hours. In order to detect and terminate orphaned database connection/sessions, sqlnet.ora sqlnet.expire_time and/or the OS level tcp_keepalive_interval should be set. The sqlnet.ora sqlnet.expire_time is not set by default and the tcp_keepalive_interval is set to ~2 hours. To set tcp_keepalive settings, see below or follow this link: How to set tcp_keepalive options. The expire_time and tcp_keepalive will not detect and terminate connections killed by JDBC Connection ReadTimeout.

Abnormal TCP connection termination can occur due to OS or VM crashes, network issues, or Firewall failures. You can easily simulate this scenario by pulling the network cable on the JDBC client system. Issuing Ctrl+C or kill -9 does not cause the issues as they both appear to cause the TCP connection to close.

Oracle® Database JDBC Developer's Guide
11g Release 2 (11.2)

Similar issues have been documented by others

Howto set tcp_keepalive settings:

Persistent changes:

Edit /etc/sysctl.conf and add/update


sudo /etc/rc.d/init.d/network restart

Temporary changes:

sudo sysctl -w net.ipv4.tcp_keepalive_time=60 net.ipv4.tcp_keepalive_intvl=6 net.ipv4.tcp_keepalive_probes=10

cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes


sudo su

echo 7200 > /proc/sys/net/ipv4/tcp_keepalive_time
echo 75 > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo 9 > /proc/sys/net/ipv4/tcp_keepalive_probes

Sqlnet.ora timeout setting:

Typical path and format:




Oracle 11g R2 EE 64 bit with Partitioning
Oracle Linux Server Release 6.2
Kernel Linux 2.6.32-300.3.1.el6uek.x86_64

sqlnet.ora sqlnet.EXPIRE_TIME set to 1
tcp_keepalive set to defaults

create table foo (name varchar2(50));
insert into foo(name) values('foo');
insert into foo(name) values('bar');

select * from foo where name='bar' for update;

set serveroutput on;
  startDate date;
  select sysdate into startDate from dual;
  select name into tmpVar from foo where name='foo' for update;
  dbms_output.put_line('total time' || numtodsinterval(sysdate-startDate, 'day'));

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcIdleTestB
  public static void main(String[] args) throws ClassNotFoundException, SQLException
    String jdbcUrl = "jdbc:oracle:thin:@";
    String user = "system";
    String pwd = "fubar1";
    //see below for formmatted version
    String lockFooAndSleepSql =
        "DECLARE startDate date := sysdate; tmpVar1; tmpVar2; waitTimeSec NUMBER := ?; endDate date; curDate date; BEGIN select sysdate+(waitTimeSec*(1/86400)) into endDate from dual; SELECT name INTO tmpVar1 FROM foo WHERE name='foo' FOR UPDATE; SELECT name INTO tmpVar2 FROM foo WHERE name='bar' FOR UPDATE;  WHILE sysdate < endDate  LOOP select sysdate into curDate from dual; END LOOP; END;";
    String waitTimeSec = "600000";
    lockFooAndSleepSql = lockFooAndSleepSql.replace("?", waitTimeSec);
    String readTimeout = "10000"; // mS
    System.setProperty("oracle.jdbc.ReadTimeout", readTimeout);

    System.out.println("Loaded driver: oracle.jdbc.OracleDriver");

    Connection conn = null;
    Statement stmt = null;
      conn = DriverManager.getConnection(jdbcUrl, user, pwd);

      stmt = conn.createStatement();

      System.out.println("Performing sql: " + lockFooAndSleepSql);
      if (conn != null)
        // System.out.println("stmt.close");
        // conn.close();

  startDate DATE := sysdate;
  waitTimeSec NUMBER := ?;
  endDate DATE;
  curDate DATE;
  SELECT sysdate+(waitTimeSec*(1/86400)) INTO endDate FROM dual;
  SELECT name INTO tmpVar2 FROM foo WHERE name='foo' FOR UPDATE;
  SELECT name INTO tmpVar1 FROM foo WHERE name='bar' FOR UPDATE;
  WHILE sysdate < endDate
    SELECT sysdate INTO curDate FROM dual;


Popular posts from this blog

Generically load enum mapping via properties file

ActiveMQ https