読者です 読者をやめる 読者になる 読者になる

見習いプログラミング日記

Java EE を中心に色々なことを考えてみます。目指せ本物のプログラマ。

PostgreSQL JDBCドライバのタイムアウト設定

OracleJDBCドライバと同様に、PostgreSQLJDBCドライバにも同様のタイムアウト設定が用意されています。@yamadamnさんがWebLogicServer + Oracle JDBC向けにまとめた資料Oracle JDBCドライバプロパティの活用を参考に、WildFly + PostgreSQL版のタイムアウト設定を以下にまとめます。

データベース接続時のタイムアウト

PostgreSQLのデータベース接続時のタイムアウトには2種類のパラメータがあります。いずれもデフォルトは未設定で、Javaのレイヤではタイムアウトの設定はされず、NW障害時やDBハングアップ時にはOSのTCP接続タイムアウトまで待ちます。

loginTimeout=<秒>

このタイムアウト設定はTCP接続のタイムアウトではなく、ログイン処理全体のタイムアウトを示します。PostgreSQLへのログイン処理は、大まかに4つのステップに分かれていますが、これら4ステップの完了までがタイムアウトの範囲です。

PostgreSQL JDBCのログイン処理の流れ
1. java.net.Socket.connectによるTCP接続
2. Startup messageの送信
(ユーザ名、接続先DB、クライアント側の文字エンコーディング送信など)
3. 認証要求
4. Initial Queryの実行 (SET extra_float_digits = 3 の実行)

loginTimeoutの値を超えてもログイン処理が完了しない場合、タイムアウト監視スレッドがログイン処理のスレッドに割込みを掛けて、以下のような例外が出力されます。

org.postgresql.util.PSQLException: 接続試行がタイムアウトしました。
	at org.postgresql.Driver$ConnectThread.getResult(Driver.java:374)
	at org.postgresql.Driver.connect(Driver.java:286)
	at java.sql.DriverManager.getConnection(DriverManager.java:664)
	at java.sql.DriverManager.getConnection(DriverManager.java:208)
	at net.agetsuma.jdbc.Main.main(Main.java:32)
connectTimeout=<秒> (9.3-1103より有効)

2015/01/02にリリースされたVersion 9.3-1103より有効なパラメータで、loginTimeoutと異なり、TCP接続時のみに着目したタイムアウトです。

具体的には、PostgreSQLに対して接続する時に実行するjava.net.Socket.connectメソッドの引数に渡しています。

// org.postgresql.core.PGStreamのコンストラクタから抜粋
Socket socket = new Socket();
socket.connect(new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort()), timeout);
loginTimeoutとconnectTimeoutのどっちを使う?

loginTimeoutはconnectTimeoutの範囲を兼ねるため、NW障害やDB過負荷を考慮したタイムアウトとしてはloginTimeoutのみで十分と思います。

ソケット読み込み時のタイムアウト

Oracleと同様に、setQueryTimeoutでは救えないSQL実行中のDBハングアップなどの障害を考慮して、ソケット読み込みタイムアウトを設定することが可能です。

socketTimeout=<秒>

このタイムアウトを超えると以下のような例外が投げられます。

org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:281)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:562)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:420)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:305)
	at nagetsu.jdbc.Main.main(Main.java:39)
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
        ...

JDBCプロパティのWildFlyデータソース設定方法

JDBCプロパティはCLIによって設定可能です。以下の例では、データソース名PostgresDSに対してloginTimeoutを60秒、socketTimeoutを600秒に設定しています。

data-source add --name=PostgresDS --jndi-name=java:/jboss/PostgresDS --driver-name=postgresql-9.3-1103.jdbc41.jar --connection-url=jdbc:postgresql://192.168.1.1/test --max-pool-size=xx --min-pool-size=xx --initial-pool-size=xx --pool-prefill=true --check-valid-connection-sql="SELECT 1" --set-tx-query-timeout=true --user-name=postgres --password=postgres --enabled=true
/subsystem=datasources/data-source=PostgresDS/connection-properties=loginTimeout:add(value=60)
/subsystem=datasources/data-source=PostgresDS/connection-properties=socketTimeout:add(value=600)

まとめ