自動クライアント・フェイルオーバーの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_Servern接続属性を使用して具体的に構成する必要があります。

ノート:

TTC_Server2TTC_Server_DSN2TTC_Servernまたは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_Servern属性の設定で指定されたリストから代替サーバーをランダムに選択します。選択したサーバーにクライアントが接続できない場合、クライアントはリストされているサーバーのいずれかに正常に接続するまでリダイレクトし続けます。0を設定すると、TimesTenはTTC_Servernサーバーのリストを順番に処理します。『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接続に登録する必要があります。

この項では、そのステップを説明します。

  1. クライアント・フェイルオーバー・イベント・リスナーの実装

  2. クライアント・フェイルオーバー・リスナー・インスタンスの登録

  3. クライアント・フェイルオーバー・リスナー・インスタンスの削除

クライアント・フェイルオーバー・イベント・リスナーの実装

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();
      }

クライアント・フェイルオーバー・リスナー・インスタンスの削除

TimesTenConnectionインタフェースにより、次のメソッドを定義してフェイルオーバー・イベント・リスナーの登録を解除します。

  • void removeConnectionEventListener (ClientFailoverEventListener listener)

リスナー・インスタンスを登録解除する場合は、このメソッドを使用します。

フェイルオーバー時のJDBCアプリケーション・アクション

フェイルオーバーが発生した場合にアプリケーションで実行されるアクションを示します。

アプリケーションのフェイルオーバーの場合の手順

アプリケーションでの操作に対して、「自動クライアント・フェイルオーバーのJDBCサポート」内の自動クライアント・フェイルオーバーに関する説明で最初に示されているいずれかのエラー条件が発生した場合は、アプリケーション・フェイルオーバーが進行中です。フェイルオーバー後にリカバリするために実行できる操作を示します。
  1. 接続のすべてのトランザクションをロールバックします。
  2. 前の接続からのすべてのオブジェクトをクリーン・アップします。前の接続に関連付けられている状態やオブジェクトはいずれも保持されません。
  3. TTC_NoReconnectOnFailover=0 (デフォルト)の場合は、次の「フェイルオーバーの遅延および再試行の設定」の項で説明されているように、短時間スリープ状態になります。TTC_NoReconnectOnFailover=1の場合は、かわりにアプリケーションを手動で代替データベースまたはデータベース要素に再接続する必要があります。
  4. 接続に関連するすべてのオブジェクトを再作成して再度準備します。
  5. 進行中のトランザクションを最初から再開します。

フェイルオーバーの遅延および再試行の設定

自動クライアント・フェイルオーバー中に別のデータベースまたはデータベース要素に再接続する場合、時間がかかることがあります。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    ...}