経緯
Db2 on CloudはIBM CloudのDBaaSの1つです。それを利用する際に必ず設定すべき接続パラメータがありますので紹介します。なお、ここで紹介するのはJDBCを利用するケースです。
enableSeamlessFailover=1
Java EEサーバでDB2に接続する場合、一般的には接続プールが使われます。接続をアプリで切断しても実際には切断されずにプール内に保管され、次に接続する際に再利用することで、時間のかかるデータベースへの接続時間を軽減することができます。
しかし、WebSphere Application ServerやWebSphere Libertyを使用しているケースで、もしプール内の接続を再利用する前にデータベースとの接続が裏で切断されてしまった場合、それを再利用しようとすると、アプリではSQLコード-4498でClientRerouteExceptionが発生します。
IBM WebSphere Application Serverの古くからのユーザーであれば、StaleConnectionExceptionのことだと言えば記憶にある方もいるかもしれません
Db2 on Cloudの場合、障害だけでなく、IBM Cloudによるメンテナンス作業等のため、Db2のアクティブ機を切り替える場合があるため、普通にしていてもこの例外はしばしば発生します。
これが発生すると、アプリ側でそれをキャッチして接続をリトライする処理を実装していない限り、アプリとしてはエラーになります。
JDBCパラメータにenableSeamlessFailover=1
を設定すると、このリトライ処理を自動的に行ってくれます。ただし全てのケースを救えるわけではありません。
https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_11.5.0/com.ibm.db2.luw.apdv.java.doc/src/tpc/imjcc_r0052038.html
IBM Data Server Driver for JDBC and SQLJはシームレス・フェイルオーバーを使用します。 このことは、以下の条件が満たされる場合には、失敗した接続が正常に再確立された後に、ドライバーは SQL エラー・コード -4498 の SQLException をスローしないことを意味しています。
障害の発生時に、接続がトランザクション用には使用されていなかった。
グローバル一時表などのグローバル・リソース、オープン状態の保留カーソル、または他のサーバーへのシームレス・フェイルオーバーを妨げる接続状態が残存しない。
シームレス・フェイルオーバーが行われる場合、新しいデータ・ソースへの接続が確立された後、ドライバーは元の接続の障害時に処理されていた SQL ステートメントを再発行します。
全てのケースは救えないですが、特にオンラインのアプリケーションではこの設定は必須と言っていいでしょう。経験上、この設定でかなりのケースを救えます。
connectionTimeout
connectionTimeoutはJDBCプロパティとConnectionManagerの両方に同名のプロパティがあるので注意が必要です。ここで言っているのはJDBCプロパティの方です。
connectionTimeoutは、データベースに接続を開始してから接続が確立するまでの待ち時間です。デフォルトはタイムアウトなしです。このパラメータも必ず設定してください。
障害でDb2 on Cloudが停止した際、TCP的には疎通可能な状態なときがあります。TCP的にはESTABLISHEDかつKeepAlivedな状態です。しかしこれはDb2として認証をして接続が確立した状態ではありません。このとき、データソースの接続プールの1つがこの認証前の接続が占有してしまいます。オンラインアプリケーションの場合、その間も接続要求を受けてしまうので、早晩、接続プールがすべて認証前の接続で埋まってしまい、新規接続要求を受け付けなくなってしまいます。そしてDb2が回復してもプール内の接続はリセットされないので、結果としてアプリは再起動しない限り永遠にデータベースに接続できなくなります。connectionTimeoutを設定することで、プール内の認証前の接続を時間で切ることができます。
blockingReadConnectionTimeout
connectionTimeoutは新規接続要求から認証含めた接続確立までの待機時間でしたが、blockingReadConnectionTimeoutはSQL実行後にサーバから応答がない場合の待機時間です。これもデフォルト値がタイムアウトなしのため、SQL実行後にDB2が停止し、TCP的に切断されなかった場合、アプリは永遠にサーバからの応答を待ってしまうリスクがあるため、それを回避するために設定します。
ただし注意点として、このパラメータは、結果が返ってくるまで時間がかかるSQLを実行した場合でも効いてしまいますので、アプリで想定する最も応答時間が長い処理よりも長く設定する必要があります。
設定例
<dataSource id="xx" jndiName="jdbc/xx" isolationLevel="TRANSACTION_READ_COMMITTED">
<jdbcDriver libraryRef="DB2JCCLib"/>
<properties.db2.jcc databaseName="BLUDB"
serverName="xxx" portNumber="xxx" user="xxx" password="xxx"
enableSeamlessFailover="1"
connectionTimeout="10s"
blockingReadConnectionTimeout="30s"
/>
</dataSource>