自動クライアント・フェイルオーバーのJDBCサポート
自動クライアント・フェイルオーバーに関連するTimesTen JDBC拡張機能を示します。
TimesTen Scaleoutの場合は、『Oracle TimesTen In-Memory Database Scaleoutユーザーズ・ガイド』のクライアント接続フェイルオーバーを参照してください。TimesTen Classicの場合は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』の自動クライアント・フェイルオーバーの使用を参照してください。開発者向けの関連情報は、『Oracle TimesTen In-Memory Database C開発者ガイド』の自動クライアント・フェイルオーバーのODBCサポートを参照してください。
自動クライアント・フェイルオーバーについて
自動クライアント・フェイルオーバーは、TimesTen ScaleoutまたはTimesTen Classicの高可用性のシナリオにおいて使用されます。TimesTen Classicには、2つのシナリオがあります。1つはアクティブ・スタンバイ・ペアのレプリケーションを使用し、もう1つは汎用自動クライアント・フェイルオーバーと呼ばれます。
クライアントが接続しているデータベースまたはデータベース要素に障害が発生した場合は、代替データベースまたは代替データベース要素にフェイルオーバー(接続転送)します。
-
TimesTen Scaleoutの場合、グリッド内の使用可能な要素のリストにある要素にフェイルオーバーします。
-
アクティブ・スタンバイ・レプリケーションを使用するTimesTen Classicの場合、新しいアクティブ(元のスタンバイ)データベースにフェイルオーバーします。
-
汎用自動クライアント・フェイルオーバーを使用するTimesTen Classicの場合、スキーマおよびデータが両方のデータベースで一貫していることを確認でき、クライアントの
odbc.ini
ファイルで構成されているリスト内のデータベースにフェイルオーバーします。汎用自動フェイルオーバーの一般的なユースケースは、読取り専用キャッシュを使用する一連のデータベースで、各データベースのキャッシュ・データ・セットは同じです。たとえば、読取り専用キャッシュ・グループが複数ある場合は、フェイルオーバー・サーバーのリストに含まれるすべてのTimesTen Classicデータベースに、同じ読取り専用キャッシュ・グループを作成します。クライアント接続が代替のTimesTenデータベースにフェイルオーバーされると、TimesTenによって(必要に応じて) Oracleデータベースからのデータが自動的にリフレッシュされるため、キャッシュ・データの一貫性が保たれます。
アプリケーションは、新規データ・データベースまたはデータベース要素に自動的に再接続されます。TimesTenは自動クライアント・フェイルオーバーが発生したときに、アプリケーションに警告を渡す機能を提供しているため、アプリケーションは適切な処理を行うことができます。
次のエラー条件はいずれも、自動クライアント・フェイルオーバーを示します。
-
SQL状態08006のネイティブ・エラー30105
-
ネイティブ・エラー47137
ノート:
-
自動クライアント・フェイルオーバーは、クライアント/サーバー接続にのみ適用されます。ここで説明する機能は、直接接続には使用できません。
-
Oracle Clusterwareを使用する場合、自動クライアント・フェイルオーバーはOracle Clusterwareに対する補完機能ですが、この2つの機能は互いに依存しません。『Oracle TimesTen In-Memory Databaseレプリケーション・ガイド』のOracle Clusterwareを使用したアクティブ・スタンバイ・ペアの管理も参考になる可能性があります。
自動クライアント・フェイルオーバーのJDBCサポートの特徴および機能
クライアント・フェイルオーバーに関連するTimesTen JDBC機能(具体的にはプール済接続に関連する機能など)を示します。
ここで説明するTimesTenのJDBCクラス、インタフェース、メソッドの詳細は、Oracle TimesTen In-Memory Database JDBC拡張Java APIリファレンスを参照してください。
一般的なクライアント・フェイルオーバー機能
自動クライアント・フェイルオーバーに関するTimesTen JDBCサポートでは、フェイルオーバーを検出するための2つのメカニズムが用意されています。
-
SQL例外での同期検出: 自動クライアント・フェイルオーバー後、障害が発生した接続に作成された文、準備済文、コール可能な文および結果セットなどのJDBCオブジェクトは使用できなくなります。そのようなオブジェクトにアプリケーションがアクセスを試みた場合、Java SQL例外がスローされます。その例外のSQL状態およびエラーコードを確認することで、例外が、フェイルオーバーの状況の結果であるかどうかを判断できます。
-
イベント・リスナーでの非同期検出: アプリケーションでは、フェイルオーバーのプロセスで発生する各イベントの通知対象となるユーザー定義のクライアント・フェイルオーバー・イベント・リスナーを登録できます。
TimesTen JDBCは、パッケージcom.timesten.jdbc
で自動クライアント・フェイルオーバーをサポートするために次の機能を提供します。
-
ClientFailoverEvent
クラスこのクラスは、クライアント・フェイルオーバー中に発生したイベント(開始、終了、中止または再試行)を表現するために使用されます。
-
ClientFailoverEventListener
インタフェースクライアントのフェイルオーバー・イベントが関係するアプリケーションでは、クライアントのフェイルオーバー・イベントをリスニングするメカニズムである、このインタフェースを実装するクラスを含める必要があります。アプリケーションの実行時に、TimesTen接続を介して
ClientFailoverEventListener
インスタンスを登録する必要があります(直下を参照)。リスナーを使用すると、障害の検出時に、接続プール文キャッシュのリフレッシュなどを行って積極的に対応できます。
-
TimesTenConnection
インタフェースのメソッドこのインタフェースはメソッド
addConnectionEventListener()
およびremoveConnectionEventListener()
を指定し、それぞれクライアント・フェイルオーバー・イベント・リスナーの登録または削除を行います。 -
TimesTenVendorCode
インタフェースの新しい定数TT_ERR_FAILOVERINVALIDATION
これは、イベントがフェイルオーバー・イベントであると確認できます。
プール済の接続のクライアント・フェイルオーバー機能
TimesTenでは、プールされた接続(javax.sql.PooledConnection
)または接続プール・データソース(javax.sql.ConnectionPoolDataSource
)を使用するアプリケーションで、前述の同期メカニズムを使用し、障害が発生した接続で失効したオブジェクトを処理することをお薦めします。
プールされた接続はJava EEアプリケーション・サーバーで管理するため、プールされた接続でのイベントをアプリケーションでリスニングすることはできません。また、アプリケーション・サーバーでは、TimesTenの拡張であるClientFailoverEventListener
のインスタンスの実装および登録を行いません。
自動クライアント・フェイルオーバーの構成
『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTen Classicの場合の自動クライアント・フェイルオーバーの構成、または『Oracle TimesTen In-Memory Database Scaleoutユーザーズ・ガイド』のクライアント接続フェイルオーバーを参照してください。
TimesTen Classicでは、フェイルオーバーDSNは、TTC_Server2
接続属性とTTC_Server
n
接続属性を使用して具体的に構成する必要があります。
ノート:
TTC_Server2
、TTC_Server_DSN2
、TTC_Server
n
またはTCP_Port2
のいずれかを設定することは、自動クライアント・フェイルオーバーを使用することを意味します。アクティブ・スタンバイ・ペアのシナリオの場合は、アプリケーションでフェイルオーバー・メカニズムをサポートできるように新しいスレッドが作成されることも意味します。
次のTimesTen接続属性に注意してください。
-
TTC_NoReconnectOnFailover
: この値が1に設定されている(有効になっている)場合、TimesTenでは自動再接続以外の、すべての通常のクライアント・フェイルオーバー処理を実行します。(たとえば、文および接続ハンドルは無効としてマークされます。)これは、アプリケーションが独自に接続プーリングを行ったり、フェイルオーバー後のデータベースへの再接続を独自に管理している場合に便利です。デフォルト値は0 (再接続)です。『Oracle TimesTen In-Memory Databaseリファレンス』のTTC_NoReconnectOnFailoverも参照してください。 -
TTC_REDIRECT
: この値が0に設定されていて、必要なデータベースまたはデータベース要素への最初の接続試行に失敗した場合、エラーが返されそれ以上の接続試行は行われません。これは、その接続におけるその後のフェイルオーバーには影響しません。『Oracle TimesTen In-Memory Databaseリファレンス』のTTC_REDIRECTも参照してください。 -
TTC_Random_Selection
: 汎用自動クライアント・フェイルオーバーを使用するTimesTen Classicの場合、デフォルト設定の1 (有効)では、フェイルオーバーが発生したときに、クライアントがTTC_Server
n
属性の設定で指定されたリストから代替サーバーをランダムに選択します。選択したサーバーにクライアントが接続できない場合、クライアントはリストされているサーバーのいずれかに正常に接続するまでリダイレクトし続けます。0を設定すると、TimesTenはTTC_Server
n
サーバーのリストを順番に処理します。『Oracle TimesTen In-Memory Databaseリファレンス』のTTC_Random_Selectionも参照してください。
ノート:
これらのいずれかをodbc.ini
または接続文字列で設定すると、設定はフェイルオーバー接続に適用されます。これらはアプリケーション(ALTER SESSION
を含む)では設定できません。
JDBCでの自動クライアント・フェイルオーバーの同期検出
フェイルオーバー時に、障害が発生した接続に作成されたオブジェクトをアプリケーションで使用しようとすると、JDBCはSQL例外をスローします。ベンダー固有の例外コードは、TimesTenVendorCode.TT_ERR_FAILOVERINVALIDATION
に設定されます。
このメカニズムによるフェイルオーバーの検出は、同期検出として示されます。次の例でこれを説明します。
try {
// ...
// Execute a query on a previously prepared statement.
ResultSet theResultSet = theStatement.executeQuery("select * from dual");
// ...
} catch (SQLException sqlex) {
sqlex.printStackTrace();
if (sqlex.getErrorCode() == TimesTenVendorCode.TT_ERR_FAILOVERINVALIDATION) {
// Automatic client failover has taken place; discontinue use of this object.
}
}
JDBCでの自動クライアント・フェイルオーバーの非同期検出
非同期のフェイルオーバーの検出には、アプリケーションでクライアント・フェイルオーバー・イベント・リスナーを実装し、そのインスタンスをTimesTen接続に登録する必要があります。
この項では、そのステップを説明します。
クライアント・フェイルオーバー・イベント・リスナーの実装
TimesTen JDBCは、イベントのリスニングに使用するcom.timesten.jdbc.ClientFailoverEventListener
インタフェースを提供します。
このインタフェースは、次の方法で強調表示されます。
-
void notify(ClientFailoverEvent
event
)
非同期フェイルオーバー検出を使用するには、このインタフェースを実装するクラスを作成した後、TimesTen接続の実行時に、クラスのインスタンスを登録する必要があります(概要が説明されています)。
フェイルオーバー・イベントが発生すると、TimesTenは、登録したリスナー・インスタンスのnotify()
メソッドをコールして、イベントに関する情報を確認することができるClientFailoverEvent
インスタンスを提供します。
次の例は、ClientFailoverEventListener
実装の基本的な形式を示しています。
private class MyCFListener implements ClientFailoverEventListener {
/* Applications can build state system to track states during failover.
You may want to add methods that talks about readiness of this Connection
for processing.
*/
public void notify(ClientFailoverEvent event) {
/* Process connection failover type */
switch(event.getTheFailoverType()) {
case TT_FO_CONNECTION:
/* Process session fail over */
System.out.println("This should be a connection failover type " +
event.getTheFailoverType());
break;
default:
break;
}
/* Process connection failover events */
switch(event.getTheFailoverEvent()) {
case BEGIN:
System.out.println("This should be a BEGIN event " +
event.getTheFailoverEvent());
/* Applications cannot use Statement, PreparedStatement, ResultSet,
etc. created on the failed Connection any longer.
*/
break;
case END:
System.out.println("This should be an END event " +
event.getTheFailoverEvent());
/* Applications may want to re-create Statement and PreparedStatement
objects at this point as needed.
*/
break;
case ABORT:
System.out.println("This should be an ABORT event " +
event.getTheFailoverEvent());
break;
case ERROR:
System.out.println("This should be an ERROR event " +
event.getTheFailoverEvent());
break;
default:
break;
}
}
}
event.getTheFailoverType()
コールは、ネストされた列挙型のクラスClientFailoverEvent.FailoverType
のインスタンスを返します。TimesTenでは唯一サポートされる値は、接続のフェイルオーバーを示すTT_FO_CONNECTION
です。
event.getTheFailoverEvent()
コールは、ネストされたクラスClientFailoverEvent.FailoverEvent
のインスタンスを返します。これは列挙型で、その値は次のいずれかにあります。
-
クライアント・フェイルオーバーが開始した場合は、
BEGIN
-
クライアント・フェイルオーバーが正常に完了した場合は、
END
-
クライアントのフェイルオーバーは失敗したが、再試行される場合は、
ERROR
-
クライアント・フェイルオーバーが中断した場合は、
ABORT
クライアント・フェイルオーバー・リスナー・インスタンスの登録
実行時、フェイルオーバー・イベント・リスナー・クラスのインスタンスを、TimesTen接続オブジェクトに登録する必要があります。そうすることで、フェイルオーバー・イベントに必要な、そのリスナー・クラスのnotify()
メソッドをコールすることができるようになります。
これを実行するため、TimesTenConnection
には、次のメソッドがあります。
-
void addConnectionEventListener (ClientFailoverEventListener
listener
)
リスナー・クラスのインスタンスを作成した後、このメソッドを使用して登録します。次の例では、接続を確立した後にリスナーを登録します。theDsn
がTimesTenクライアント/サーバー・データベースのJDBC URLであり、theCFListener
がフェイルオーバー・イベント・リスナー・クラスのインスタンスであると仮定します。
try {
/* Assume this is a client/server conn; register for conn failover. */
Class.forName("com.timesten.jdbc.TimesTenClientDriver");
String url = "jdbc:timesten:client:" + theDsn;
theConnection = (TimesTenConnection)DriverManager.getConnection(url);
theConnection.addConnectionEventListener(theCFListener);
/* Additional logic goes here; connection failover listener is
called if there is a fail over.
*/
}
catch (ClassNotFoundException cnfex) {
cnfex.printStackTrace();
}
catch (SQLException sqlex) {
sqlex.printStackTrace();
}
フェイルオーバー時のJDBCアプリケーション・アクション
フェイルオーバーが発生した場合にアプリケーションで実行されるアクションを示します。
アプリケーションのフェイルオーバーの場合の手順
- 接続のすべてのトランザクションをロールバックします。
- 前の接続からのすべてのオブジェクトをクリーン・アップします。前の接続に関連付けられている状態やオブジェクトはいずれも保持されません。
TTC_NoReconnectOnFailover=0
(デフォルト)の場合は、次の「フェイルオーバーの遅延および再試行の設定」の項で説明されているように、短時間スリープ状態になります。TTC_NoReconnectOnFailover=1
の場合は、かわりにアプリケーションを手動で代替データベースまたはデータベース要素に再接続する必要があります。- 接続に関連するすべてのオブジェクトを再作成して再度準備します。
- 進行中のトランザクションを最初から再開します。
フェイルオーバーの遅延および再試行の設定
自動クライアント・フェイルオーバー中に別のデータベースまたはデータベース要素に再接続する場合、時間がかかることがあります。TimesTenでそのクライアントのフェイルオーバー・プロセスが完了する前に、アプリケーションによってリカバリ・アクションが試みられると、別のフェイルオーバー・エラー条件が発生する可能性があります。
「自動クライアント・フェイルオーバーのJDBCサポート」を参照してください。
したがって、アプリケーションでは後続の各試行に先立つ、多少の遅れを伴う1度のループにおいてすべてのリカバリ・アクションを行う必要があり、試行の合計数には制限を設けます。試行回数を制限しないと、クライアント・フェイルオーバー・プロセスが正常に完了しなかった場合にアプリケーションがフリーズしたように見える場合があります。たとえば、リカバリ・ループについて再試行の遅延を100ミリ秒、最大再試行回数の制限を100回にできます。理想的な値はそれぞれのアプリケーションおよび構成によって異なります。
同期検出方法を使用する次のコード例は、JDBCアプリケーションの接続フェイルオーバー・エラーの再試行を処理する方法を示しています。直接関係のないコードは省略(...)
されています。
// Database connection object
Connection dbConn;
// Open the connection to the database
...
// Disable auto-commit
dbConn.setAutoCommit( false );
...
// Prepre the SQL statements
PreparedStatement stmtQuery = dbConn.prepare("SELECT ...");
PreparedStatement stmtUpdate = dbConn.prepare("UPDATE ...");
...
// Set max retries to 100
int retriesLeft = 100;
// and retry delay to 100 ms
int retryDelay = 100;
// Records outcome
boolean success = false;
Boolean needReprepare = false;
// Execute transaction with retries until success or retries exhausted
while ( retriesLeft > 0 )
{
try {
// Do we need to re-prepare
if ( needReprepare )
{
Thread.sleep( retryDelay ); // delay before proceeding
stmtQuery = dbConn.prepare("SELECT ...");
stmtUpdate = dbConn.prepare("UPDATE ...");
needReprepare = false;
}
// First execute the query
// Set input values
stmtQuery.setInt(1, ...);
stmtQuery.setString(2, ...);
// Execute and process results
ResultSet rs = stmtQuery.executeQuery();
while ( rs.next() )
{
int val1 = rs.getInt(1);
String val2 = rs.getString(2);
...
}
rs.close();
rs = null;
// Now execute the update
// Set input values
stmtUpdate.setInt(1,...);
stmtUpdate.setString(2,...);
// Execute and check number of rows affected
int updCount = stmtUpdate.executeUpdate();
if ( updCount < 1 )
{
...
}
// And finally commit
dbConn.commit();
// We are done
success = true;
break;
} catch ( SQLException sqe ) {
if ( (sqe.getErrorCode() == 47137) ||
( (sqe.getErrorCode() == 30105) && (sqe.getSQLState().equals("08006")) ) )
// connection failover error
{
// decrement retry count
retriesLeft--;
// rollback the transaction ready for retry
dbConn.rollback();
// and indicate that we need to re-prepare
needReprepare = true;
}
else
{
// handle other kinds of error
...
}
}
} // end of retry loop if ( ! success ){ // Handle the failure ...}