11 OCIでの高可用性

高可用性は最新のアプリケーション設計の基礎であり、これにより、想定外の障害が発生してもシステムが稼働し続けリジリエンスが保たれるようになります。Oracle Call Interface (OCI)を活用する環境では、一貫したパフォーマンスを実現し、停止時間を最小限に抑え、重要なデータへのシームレスなアクセスを維持するために、堅牢なHA戦略が不可欠です。この章では、OCIでの高可用性(HA)機能について説明します。この機能は、計画メンテナンスと計画外停止の両方の間にワークロードを迅速かつ自動的にシフトするようにアプリケーションを構成するために役立ちます。アプリケーションの高可用性要件に応じて、必要なレベルの高可用性構成を実装できます。

11.1 高可用性アプリケーションの構築のベスト・プラクティス

この項では、Oracle Databaseと対話するための低レベルAPIであるOCIを使用して高可用性アプリケーションを構築するための、プログラミング・ベスト・プラクティスについて概要を示します。新規アプリケーションと既存アプリケーションの両方について課題と考慮事項を取り上げて、開始点に関係なく、開発者とシステム・アーキテクトのための実用的なガイダンスを提供します。

この項の内容は次のとおりです:

11.1.1 構成上の考慮事項

開始点として、アプリケーションに変更を加える必要はありません。新規アプリケーションの場合も既存アプリケーションの場合も、「HAレベル1 - 基本的なアプリケーション高可用性」の章で説明されている構成ステップと通知ステップに従います。

ノート:

構成上の考慮事項は、新規アプリケーションと既存アプリケーションの両方にとって、不可欠な前提条件です。

11.1.2 既存のアプリケーション

既存アプリケーションのHA機能の強化では、確立されたコード・ベースおよびインフラストラクチャの制約の範囲内で作業することが必要になります。

ベスト・プラクティスは次のとおりです:
  • アクティブ・セッションとアイドル・セッションの管理に明示的に対処します。これは、次の方法で実現できます:
    • OCIセッション・プールを利用するか、明示的なOCIRequestBegin()およびOCIRequestEnd()コールを組み込みます。
    • フェイルオーバー、ロード・バランシングおよびセッション永続性のサポートを段階的に追加することで、システムの全面的な改修なしでHA機能を改良できます。
  • HAレベル2 - 計画メンテナンスへの対応について:
    • 特定のホスト名ではなくサービスを使用するように接続文字列を更新して、メンテナンスの間の動的なルーティングを可能にします。
    • 計画メンテナンスをサポートするようにHA機能を改良します。
    • セッション・プーリングを使用するか、OCIRequestBegin()OCIRequestEnd()を使用して各アプリケーション・リクエストをバインドします。

      ノート:

      孤立したコールをなくすには、すべてのOCI処理がOCIRequestBegin()OCIRequestEnd()の内側にある必要があります。

    「HAレベル2 – 計画メンテナンスへの対応」の詳細は、高可用性概要およびベスト・プラクティス・ガイド - レベル2: 計画メンテナンスに向けたアプリケーションの準備の構成を参照してください

  • HAレベル3 - 計画外停止と計画フェイルオーバーへの対応について:
    • 自動フェイルオーバー・ルーティングのために複数のデータベース・インスタンスを含むように、接続文字列をアップグレードします。
    • ユーザーへの影響なしでセッションが代替インスタンスに再接続されるようにするには、次のフェイルオーバー機能のどれかを使用します:
      • 基本的なフェイルオーバー機能を有効にするための最小限のコード変更による、透過的アプリケーション・フェイルオーバー(TAF)のサポート。
      • 停止をマスクし、データベース・リクエストを透過的にリプレイして、トランザクション整合性とセッション状態を維持することで、シームレスなユーザー・エクスペリエンスを促進する、アプリケーション・コンティニュイティ(AC)。
      • セッションがユーザーへの影響なしで代替インスタンスに再接続できるようにするために役立つ、透過的アプリケーション・コンティニュイティ(TAC)

    ACとTACでは、再接続後にアクティブ・トランザクションをリプレイするためのサポートがさらに含まれています。通知を構成して、データベースの変更点についてアプリケーションにアラートを送信することでシームレスなフェイルオーバー、エラー・リカバリおよび効率的なリソース管理を可能にします。詳細は、高可用性概要およびベスト・プラクティス - レベル3: 計画外および計画フェイルオーバーのアプリケーションからのマスクの構成を参照してください

11.1.3 新規アプリケーション

高可用性は、アーキテクチャにゼロから組み込むことができます。これにより、開発者は、レガシー・システムの制約なしで、最新のOracle Database機能とベスト・プラクティスを活用できるようになります。

ベスト・プラクティスは次のとおりです:

11.2 高可用性構成レベル

OCIには次の3つのタイプの高可用性構成レベルがあります。各レベルの詳細を次に示します。アプリケーションの高可用性要件に応じて、必要なレベルの高可用性(HA)保護を実装できます:

11.2.1 HAレベル1 - 基本的なアプリケーション高可用性

この項では、計画外停止と計画停止の場合の停止時間を最小限に抑えるための、構成と通知について説明します。レベル1のステップでは、アプリケーションの変更は必要ありません。

ステップ1: 高可用性データベース・サービスの構成

データベースがHAであることを確認します。データベース・サービスの構成の詳細は、「高可用性データベース・サービスの構成」を参照してください。

ステップ2: 高可用性のための接続文字列の構成

接続文字列の構成の詳細は、「高可用性のための接続文字列の構成」を参照してください。

また、パフォーマンスと可用性のために、オペレーティング・システムのTCPとOracle Net構成を構成します。

Oracle Database Net Servicessqlnet.oraファイルの次のパラメータを確認します:
  • その他のOracle Database Net Servicesパラメータも、高可用性とパフォーマンスのチューニングに役立ちます:

    • たとえば、データベースのlistener.oraファイルには、接続ストームへの対応に役立つ、オプションのRATE_LIMITおよびQUEUESIZEパラメータがあります。
    • Oracle Client 18cでは、EXPIRE_TIMEパラメータtnsnames.ora接続記述子で使用すると、ファイアウォールでアイドル接続が終了されなくすることや、キープ・アライブのタイムアウト(EXPIRE_TIME=n)を調整することができます。EXPIRE_TIMEについては、終了期間の半分よりわずかに少ない値を使用することが一般的に推奨されています。旧バージョンのOracle Clientでは、tnsnames.ora接続記述子のオプションENABLE=BROKENEXPIRE_TIMEのかわりに使用できます。これらの設定は、終了したリモート・データベース・サーバーの検出にも役立ちます。
    • Oracle Client 19cのライブラリでは、EXPIRE_TIMEは、Easy Connect: host/service?expire_time=nを介して使用できます。Oracle Client 21cのライブラリでは、これは、クライアント側のsqlnet.ora内で使用できます。
ステップ3: アプリケーションがHA通知を受信していることの確認
  • 計画メンテナンス・アクティビティに関する通知は、Oracle Client 19c以降を使用してアプリケーションにインバンドで送信されます。
  • オプションで、オンプレミス・データベースとADB-Dの場合は、高速アプリケーション通知(FAN)を有効にします。FANの有効化の詳細は、「FANが使用されていることの確認」を参照してください。
    OCI FANの要件
    • データベース・サービス構成: データベース・サービス属性-notification TRUEを設定します。
    • FANイベントの通知構成: OCIクライアントは、すべての接続(プールに属していないものも含む)について、FANイベントに対応します。次の2つのオプションが使用可能です。
      • OCI_EVENTSモードを使用します。例:
        ... OCIEnvCreate(&envhp, OCI_EVENTS, 0, 0, 0, 0, 0, 0);
      • oraaccess.xmlを使用し、イベント・タグをTRUEに設定します。例:
        <oraaccess 
              xmlns="http://xmlns.oracle.com/oci/oraaccess" 
              xmlns:oci="http://xmlns.oracle.com/oci/oraaccess" 
              schemaLocation="http://xmlns.oracle.com/oci/oraaccess 
                              http://xmlns.oracle.com/oci/oraaccess.xsd"> 
           <default_parameters> 
         	<events>TRUE</events> 
           </default_parameters> 
        </oraaccess>
        

      関連項目:

ステップ4: アプリケーションへの再接続ロジックの実装の確認

再接続ロジックの実装の詳細は、「アプリケーションへの再接続ロジックの実装の確認」を参照してください。

11.2.2 HAレベル2 - 計画メンテナンスへの対応

アプリケーションの「HAレベル1 - 基本的なアプリケーション高可用性」に基づくレベル2では、計画メンテナンスの間のアプリケーションへの影響を最小限に抑えるために高可用性構成が追加されます。レベル1を実装すれば、計画メンテナンスのHA構成を実装する準備は整います。

計画メンテナンスとは、停止時間やアプリケーションの可用性の阻害を最小限に抑えて、データベース・システムを正常に保つことを目的とした、スケジュールされたメンテナンス・アクティビティのことを指します。計画メンテナンスは、通常、データベース管理者(DBA)、クラウド・サービス・プロバイダ(マネージド・サービスの場合)またはIT運用チームによって開始されます。

ユーザーへのアプリケーションに関する影響を最小限に抑えて計画メンテナンスに対応するには、OCIセッション・プーリング、セッション排出を利用するか、明示的なリクエスト境界を使用します。次のプラクティスを採用して、アプリケーションの高可用性をレベル2に引き上げます:
  • OCIセッション・プーリングの使用

    OCIセッション・プールにより、計画メンテナンスの間に接続の再試行とリダイレクトがシームレスに管理されます。データベース・インスタンスがパッチ適用を受ける場合は、OCIによって、制御された期間にわたり、使用可能な別のインスタンスに自動的に接続がリダイレクトされます。OCIセッション・プールにより、接続が計画停止PLANNED DOWNイベントの影響を受けているタイミングが検出され、その接続はプールに戻ると終了されます。PLANNED DOWNイベントは、FANによって生成されトリガーされます。これらのイベントは、停止の間にトリガーされて、インスタンスつまり接続のステータスがセッション・プールに通知されます。

    セッション・プーリングを使用しないアプリケーションの場合は、OCIRequestBegin()OCIRequestEnd()をコールして、接続が使用されているタイミングをマークすることで、アプリケーション・リクエストの開始と終了を明示的に示します。この手法により、メンテナンスの間にユーザーの操作が正常に処理されるようになり、システムのパフォーマンスと信頼性を維持しながら中断やエラーを防ぐことでユーザー・エクスペリエンスが向上します。どちらの場合でも、OCIにより、DML文のリプレイが安全であるタイミングが暗黙的に判断され、アプリケーションで停止イベント後に認識されるエラーの数がわずかになります。

  • リクエスト境界

    リクエスト境界とは、アプリケーション・リクエストが処理される範囲または制限のことを指します。セッション・プールを使用する環境では、リクエスト境界は、プールが接続の使用状況を追跡するために役立ちます。アプリケーションは、リクエスト境界の終了をマークするために、データベース・リクエストの完了時にOCIセッション・プールに接続を戻す必要があります。

    リクエスト境界は、明示的にも暗黙的にもできます。暗黙的なリクエスト境界は、アプリケーションを変更することなく、適切な時点で、セッションにおいて自動的に想定される場合があります。明示的なリクエスト境界はACおよびTACと統合され、暗黙的なリクエスト境界はTACサービスの使用を必要とします。詳細は、次の項「HAレベル3 – 計画外停止と計画フェイルオーバーへの対応」を参照してください。

  • セッション排出

    セッション排出により、アクティブ・セッションがインスタンス停止前に正常に移動またはクローズされるようになります。

これらの方策を活用することで、HA環境では、重要なメンテナンス・アクティビティの間であっても、中断を最小限に抑えて、継続的なサービスが維持されます。

11.2.3 HAレベル3 - 計画外停止と計画フェイルオーバーへの対応

この項では、アプリケーションから計画外フェイルオーバーと計画フェイルオーバーをマスクするためと、タイムアウトと停止に対応するための高可用性構成を追加します。

計画外停止は、データベース・システムやその基にあるインフラストラクチャでの想定外の障害または中断であり、Oracle Call Interface (OCI)を介してアクセスされるデータベース・サービスの可用性に影響します。アプリケーションまたはデータベースの構成によって効率的に管理されていない場合は、このことより、接続が失われるか、リクエストが失敗する可能性があります。

データベース・フェイルオーバー機能(AC、TAC、TAF)

次のOracleデータベース・フェイルオーバー機能では、OCIで計画外停止に対応するための最良の方法が提供されます。これらの機能により、既知のポイントからセッションを再構築およびリカバリできるようになります。その後、それらによって、進行中の中断された処理がリプレイされます(コミットされたトランザクションはリプレイされません)。リプレイが完了すると、割込みの発生がなかったかのようにアプリケーションにトランザクションの結果が戻されます。
  • アプリケーション・コンティニュイティ(AC)

    アプリケーション・コンティニュイティにより、トランザクション整合性とセッション状態を維持しながら、停止をマスクし、データベース・リクエストを透過的にリプレイすることで、シームレスなユーザー・エクスペリエンスが実現されます。

  • 透過アプリケーション・コンティニュイティ(TAC)

    透過的アプリケーション・コンティニュイティでは、アプリケーションに変更を加える必要なく、停止の間にセッション状態とトランザクション整合性が自動的に維持されます。

  • 透過的アプリケーション・フェイルオーバー(TAF)

    透過的アプリケーション・フェイルオーバーでは、障害発生中にセッションがスタンバイ・データベースに、またはRACクラスタ内の別のインスタンスに自動的に再ルーティングされ再接続されて、ユーザーに対して中断が最小限に抑えられます。

計画フェイルオーバー

計画フェイルオーバーとは、データベース操作の、あるデータベース・インスタンスから別のデータベース・インスタンスへの制御された意図的な移行を指し、それにより、サービスの継続性を確保します。この制御された手法により、フェイルオーバー・プロセスが想定どおりに動作することを確認できます。

計画フェイルオーバーでは、Oracle DatabaseとOCIのフェイルオーバー機能が活用されて、アプリケーションで排出が実装されていないときや割り当てられた期間内に排出できないときであってもメンテナンス・エクスペリエンスが向上します。

移行の間のデータ損失を防ぐため、OCIにより、フェイルオーバーの開始前に、スタンバイ・データベースが一貫した状態でありプライマリ・データベースと完全に同期されていることが確認されます。これが確認されると、OCIによってそのスタンバイ・データベースが昇格されて新しいプライマリになります。フェイルオーバー後、それらの接続エンドポイントは、構成に応じて、手動または自動で更新されます。これにより、クライアント・アプリケーションで、新しいプライマリ・データベースへのトラフィックの転送を開始できるようになります。スタンバイ・データベースはすでに同期されているため、最小限のサービス中断でフェイルオーバーが発生して、スムーズな移行が保証されます。計画フェイルオーバーが完了したら、すべてのシステムがその新しい構成で正常に機能していることを確認してください。

元のプライマリ・システムの準備が整ったら、reverse switchoverを実行して、操作を元の構成に戻すことができます。

関連項目:

11.2.3.1 OCIとアプリケーション・コンティニュイティ

アプリケーション・コンティニュイティ(AC)により、停止の間に進行中トランザクションを自動的にリプレイしてアプリケーションの状態をシームレスに取り戻すことで、高可用性(HA)関連のアプリケーション・エラーの発生率が低減されます。

ACは、Oracle RAC、Oracle RAC One、またはActive Data Guardをインスタンスまたはサイトのフェイルオーバー用に実行しているHA環境でハードウェア、ソフトウェア、ネットワーク、ストレージのエラーとタイムアウトをマスキングします。ACでは、次のものがサポートされています:
  • SQL*Plus
  • Tuxedo
  • WebLogic Server
  • JDBCタイプ4 (Oracle Thin)
  • python-oracledb (Thick)
  • node-oracledb (Thick)
  • OCI、ODPI-Cドライバ、およびOCIとODPI-C上に構築されたアプリケーション
  • Oracle Data Provider for .NET (ODP.NET)の管理対象ドライバおよび管理対象外ドライバ。

アプリケーション・コンティニュイティは、Oracle Databaseセッション・プールを使用するOLTPアプリケーションの場合や、明示的なリクエスト境界を提供する場合にお薦めしています。アクセス制御(AC)は、アプリケーションでデータベースへの接続に使用されているデータベース・サービスに対して有効になっています。ACは、1つのどのリクエスト内でも、最初のトランザクションのコミットまでサポートされます。

11.2.3.1.1 アプリケーション・コンティニュイティの有効化方法

ACは、Oracle RACまたはData Guardの設定に依存しています。これにより、スタンバイ・データベースが使用可能かつプライマリ・ワークロードと完全に同期されている状態になります。それらの接続エンドポイントを、再ルーティングをサポートするように構成する必要があります。

ACは、クライアントの接続記述子またはドライバ構成で、特定のパラメータを設定することで有効になります。また、中断前に再接続が試みられる回数をクライアントで把握できるように、再試行の回数と再試行間の遅延に関連するパラメータを構成できます。接続プールにより、失われた接続が検出され、新しい接続が自動的に確立され、必要に応じてセッション状態が取り戻されます。

11.2.3.1.2 アプリケーション・コンティニュイティが最も効果的な状況

OCIでのアプリケーション・コンティニュイティは、アプリケーションでアプリケーション・リクエストの開始と終了を明示的にマーク(OCIRequestBegin()OCIRequestEnd()をコール)できる場合やOCIセッション・プールを使用して接続の取得と解放によって暗黙的にマークできる場合に最も効果的になります。

11.2.3.1.3 OCIのアプリケーション・コンティニュイティを無効化する要因

その後のアプリケーション要求が開始するまで、OCIのアプリケーション・コンティニュイティを暗黙的に無効化する要因を示します。

次の状況では、その後のアプリケーション・リクエストが開始されるまで、OCIでのアプリケーション・コンティニュイティは暗黙的に無効化されます:

  • データベース層(サーバー)によって、リプレイと一貫性のない状況が検出される。たとえば、PL/SQL無名ブロックに埋込みのトップレベルCOMMIT文が含まれている場合は(自律型トランザクションはトップレベルとはみなされない)、ドライバによって、OCIでのアプリケーション・コンティニュイティが暗黙的に無効化されます。

  • OCIのアプリケーション・コンティニュイティでサポートされないOCI関数をアプリケーションがコールした場合。このような関数の1つはOCIStmtPrepare()です。HAインフラストラクチャでアプリケーション・コンティニュイティの使用をサポートするには、OCIStmtPrepare2()コールを使用します。

  • オブジェクトやLOBロケータなどの記述子ベース・タイプのストリーム・バインドまたは定義。

アプリケーションでは、OCIRequestDisableReplay()をコールすることで、OCIのアプリケーション・コンティニュイティを明示的に無効化できます。

11.2.3.1.4 アプリケーション・コンティニュイティで発生する可能性のある副作用

OCIでのアプリケーション・コンティニュイティでは、リカバリ可能なエラーの後に、セッションが再構築されデータベースの状態が取り戻されると、元のPL/SQL文およびSQL文がリプレイされます。元の実行に副次的影響(Eメールの送信や印刷など、そのセッションおよびトランザクションの外部のアクション)があり、その操作がリプレイされる場合は、それらの副次的影響が繰り返される可能性があります。アプリケーションは、これらの副次的影響に対応するようにコーディングし、重複する実行を許容可能かどうか判断する必要があります。許容可能でない場合は、アプリケーションで、リプレイの影響を受容または緩和するためのアクションを実行する必要があります。たとえば、OCIRequestDisableReplay()をコールします。

関連項目:

.
11.2.3.1.5 アプリケーション・コンティニュイティに関するサポートされているOCI関数

この項では、OCIでのアプリケーション・コンティニュイティによって停止中にフェイルオーバーを実行できる関数を説明します。

OCIのアプリケーション・コンティニュイティは、次のいずれかの関数の実行時に停止が発生した場合にフェイルオーバーできます。
11.2.3.2 透過的アプリケーション・コンティニュイティ(TAC)のサポート

透過的アプリケーション・コンティニュイティ(TAC)は、リカバリ可能な停止の後にデータベース・セッションをリカバリできるように、セッションとトランザクションの状態を透過的に追跡し記録する、アプリケーション・コンティニュイティの機能モードです。

これは手動操作なしで自動的に実行されるため、DBAにアプリケーションの知識は必要なく、開発者がアプリケーション・コードを変更する必要はありません。透過性は、状態を追跡するインフラストラクチャを使用して実現されます。このインフラストラクチャは、アプリケーションがユーザー・コールを発行すると、セッション状態の使用状況を分類します。デフォルトとして透過的アプリケーション・コンティニュイティを有効にすると、計画メンテナンス時および計画外停止が発生したときにアプリケーションを保護できます。透過的アプリケーション・コンティニュイティでは、次のことが自動的に実行されるため、アプリケーションに変更を加える必要はありません:
  • PRESET状態をリストアします
  • セッションのリカバリ時に、アプリケーション・レベルの副作用を認識して無効にします。
データベース層とOCIアプリケーション層により、トランザクションおよびセッション状態の使用状況が追跡されます。これにより、OCIで、可能なリクエスト境界を検出し挿入できるようになります。

透過的アプリケーション・コンティニュイティ(TAC)は、構成パラメータFAILOVER_TYPE=AUTOを使用して有効化されます。この構成パラメータは、クライアント側でtnsnames.oraファイルを使用して設定するか、Oracle RACの場合はデータベース・サーバー側でSRVCTLまたはDBMS_SERVICEパッケージを使用して設定します。

関連項目:

.
11.2.3.3 OCIでの透過的アプリケーション・フェイルオーバー(TAF)

透過的アプリケーション・フェイルオーバー(TAF)はクライアント側の機能であり、インスタンスまたはネットワークの障害によりデータベース接続に失敗すると発生するエンドユーザー・アプリケーションに対する障害を最小限にするために設計されています。

TAFでは、存続しているデータベース・インスタンスにアプリケーション接続が自動的に再ルーティングされます。

TAFは、データベース・セッションを復元するように、また、オプションで、オープン状態(行取得プロセスの間)の問合せをリプレイするように構成できます。オープン状態の問合せをリプレイすると、読取り専用のアプリケーションが手動操作なしで動作し続けるようにするために役立ちます。DML操作を使用するアプリケーションでは、かわりにACまたはTACの使用を検討する必要があります。

TAFは、Oracle Real Application Clusters (Oracle RAC)とOracle Data Guardフィジカル・スタンバイ・データベースなど、各種のシステム構成に実装できます。TAFは、単一インスタンス・システムの再起動後(修復時など)にも使用できます。

障害発生後にアプリケーションで実行が試みられるすべての文で、TAFフェイルオーバー機能が使用されます。つまり、障害が発生した後、アプリケーションで実行が試みられる文はすべて、自動的にTAFリカバリに参加します。これには、障害発生時に失敗した文のみでなく、障害発生後にアプリケーションで実行が試みられるその他の文もすべて含まれます。後続の文が成功する場合や、アプリケーションで、試みられたTAFリカバリに対応するエラー(ORA-25401など)が受信される場合があります。アプリケーション開発者は、これらのエラーへの対処方法(アプリケーション内にエラー処理ロジックを実装するか、アラート・メカニズムを使用してユーザーに通知するかのどちらか)を決定する必要があります。最初から問合せを再実行するという方法で潜在的なエラーに対処するには、アプリケーション・ロジックを構築する必要があります。

ノート:

フェイルオーバーの発生時にコールバックを使用してセッションを必要な状態にリストアできるように、アプリケーションでコールバックを登録することをお薦めします。

ノート:

TAFは、次のものに対してはサポートされていません:
  • リモート・データベース・リンク
  • すでに進行中のトランザクションがあるセッション
  • 選択リストの一部であるLOB列
11.2.3.3.1 透過的アプリケーション・フェイルオーバーの構成

透過的アプリケーション・フェイルオーバー(TAF)は、データベース層とOCIアプリケーション層の両方で構成できます。両方が構成されている場合は、データベース層の設定が優先されます。

クライアント側でTAFを構成するには、接続記述子(たとえば、tnsnames.oraファイル内)のCONNECT_DATA部分で、FAILOVER_MODEパラメータをSESSIONまたはSELECTに設定します。

サーバー側でTAFを構成するには、FAILOVER_MODESESSIONまたはSELECTに設定します。Oracle Clusterware、Oracle RestartまたはOracle Global Data Servicesによって管理されているサービスの場合は、srvctl modify serviceを使用します。その他の、あまり管理されていないサービス(単一インスタンス・データベースで定義されているサービスなど)の場合は、DBMS_SERVICE.MODIFY_SERVICEパッケージ・プロシージャを使用します。

クライアントまたはサービスのどちらで構成する場合でも、TAFには次の2つのモードがあります:
  • SESSION - 接続とセッションのみが再確立されます。既存のカーソルをすべて再実行する必要があります。
  • SELECT - セッションが復元されます。また、アプリケーションで、以前にアクティブだったカーソルからのフェッチが試みられると、OCIによってそのカーソルが再実行され、その元の行セットの場所への復元が試みられます。このプロセスは、フェッチ・コール時にアクティブであるセッション状態に基づいて発生します。新しい行セット(またはその有効な順序)が元の実行と異なる場合は、フェッチに失敗します。

最初のフェイルオーバーは成功しないことがあります。OCIでは、失敗した後にフェイルオーバーを再試行するメカニズムを提供しています。

関連項目:

11.3 OCIでのトークンベース認証のフェイルオーバー・サポート

この項では、OCIでのトークンベース認証のフェイルオーバー・サポートについて説明します。
トークンベース認証を使用する場合は、フェイルオーバーが発生するまでに元のトークンが期限切れになる可能性があります。そのため、アプリケーションでは、更新したトークンをフェイルオーバー・プロセスの間に提供する方法が必要です。たとえば、トークンがオペレーティング・システム・ファイルに格納されている場合は、フェイルオーバー発生時に新しい有効期限詳細でそれを更新する必要があります。これは、フェイルオーバー・コールバック機能(「フェイルオーバー・コールバック」の項を参照)を使用して実現できます。データベース・トークンの提供: 更新したトークンは、次の2つの方法のどちらかで提供できます:

11.3.1 プログラムによるデータベース・トークンの提供

キャッシュされたトークンが期限切れになった場合は、セッション・ハンドルで属性OCI_ATTR_TOKENおよび OCI_ATTR_IAM_PRIVKEYを指定してOCIAttrSet()をコールすることで、そのフェイルオーバー・コールバックにおいて、最新のトークンとキーを指定する必要があります。これらは、フェイルオーバーの完了後にOCIによってキャッシュされます。

フェイルオーバー・イベントOCI_FO_BEGIN_EXPIREDTOKENが呼び出されて期限切れのトークンが更新されます。

例11-1 トークンベース認証に対応するためのTAFコールバック関数

sb4 callback_fn(dvoid *svchp, dvoid *envhp, dvoid *fo_ctx, ub4 fo_type, ub4 fo_\
event)
{
  if (fo_event == OCI_FO_BEGIN_EXPIREDTOKEN)
  {
    /* Set new token attributes */
    OCISession *usrhp=NULL;
    OCIError *ehp=NULL;
    OCIHandleAlloc ((dvoid *)envhp, (dvoid **)&ehp,
                    OCI_HTYPE_ERROR, 0, (dvoid *)0);
    OCIAttrGet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX,
               (dvoid *)&usrhp, (ub4 *)0, (ub4)OCI_ATTR_SESSION, ehp);
    if (usrhp)
    {
      getToken(token, &tokenLen, privateKey, &privateKeyLen);
      OCIAttrSet((dvoid *) usrhp, (ub4) OCI_HTYPE_SESSION,
                 (dvoid *) token, (ub4) tokenLen,
                 OCI_ATTR_TOKEN, ehp);
      OCIAttrSet((dvoid *) usrhp, (ub4) OCI_HTYPE_SESSION,
                 (dvoid *) privateKey, (ub4) privateKeyLen,
                 OCI_ATTR_IAM_PRIVKEY, ehp);
    }
    OCIHandleFree((dvoid *) ehp, OCI_HTYPE_ERROR);
  }
}

例11-2 トークンの取得

void getToken(char *token[], sb4 *tokenLen, char *privateKey[], char *tokenLoc)
{
  token_file_loc = 'tokenFile.txt';
  private_key_file_loc = 'privateKey.pem';
  fp = fopen(token_file_loc, "r");
  if (fp != NULL) {
    size_t newLen = fread(token, sizeof(char), MAXBUFLEN, fp);
    token[newLen++] = '\0';
  }
  *tokenLen = newLen;
  fclose(fp);
  fp = fopen(private_key_file_loc, "r");
  if (fp != NULL)
  {
    while ((rlen = getline(&line, &len, fp)) != -1 && line != NULL)
    {
      /* skip lines containing PEM delimiters */
      if ( strstr( line, "--BEGIN ") != NULL) {
        start = TRUE;
        continue;
      }
      else if ( strstr( line, "--END ") != NULL) {
        start = FALSE;
      break;
      }
      if (!start)
        continue;
      /* remove the \n */
      line[strlen((const char *)line) - 1] = '\0';
      strcat((char *)privateKey, line);
      pvreadLen += strlen((const char *)line);
  }
  *privateKeyLen = pvreadLen;
  return;
}

11.3.2 ファイルでのデータベース・トークンの提供

トークン・ファイルの場所は、初期セッションおよびフェイルオーバー後のセッションを確立するときに、接続文字列で指定する必要があります。

ノート:

トークンは常にファイルから読み取られ、キャッシュされません。

アプリケーション管理者は、最新のトークンを使用してトークンの場所を最新の状態に保つ必要があります。

11.4 プログラムに関する高度な考慮事項

この章では、より複雑な状態に対処するための高可用性構成について説明します。ほとんどのアプリケーションでは、次の章で詳しく説明するステップに従うことで、計画メンテナンスと計画外メンテナンスを保証できます:

より複雑な状態への対処に関するさらに詳しいインサイトを得るには、次の項を参照してください:

11.4.1 高可用性イベント通知

Oracle RACデータベースに接続された高可用性のクライアントにデータベース障害が発生したときに、HAイベント通知を使用して、ベストエフォート型プログラム・シグナルをクライアントに提供します。

バックエンド・データベース・サーバーにアクセスするアプリケーション・サーバーにログインするのにWebブラウザをユーザーが使用するとします。データベース・インスタンスに障害が発生した場合、障害がユーザーに通知されるまでに最大で数分かかる場合があります。データベース層のインスタンスの障害を迅速に検出し、これをクライアントに通知し、接続をクローズし、接続プール内のアイドル状態の接続をクリーン・アップする機能が、HAイベント通知によって提供されます。

Oracle RACデータベースに接続された項可用性のクライアントの場合、データベース障害が発生したときに、HAイベント通知を使用して、ベストエフォート型プログラム・シグナルをクライアントに提供できます。クライアント・アプリケーションは、環境ハンドルにコールバックを登録し、この情報に興味があることを伝えます。このクライアントによって確立された接続に重大な障害イベントが発生すると、そのイベントに関する情報(イベント・ペイロード)や、障害によって切断された接続(サーバー・ハンドル)のリストとともにこのコールバックが呼び出されます。

たとえば、インスタンスAに対する2つの接続と、同じデータベースのインスタンスBに対する2つの接続を持つクライアント・アプリケーションを考えてみます。インスタンスAに障害が発生すると、イベント通知がクライアントに送信され、次に、クライアントにより、インスタンスBに対する2つの接続が切断され、かつ登録されているコールバックが呼び出されます。この場合、同じデータベースの別のインスタンスCに障害が発生しても、クライアントには通知されません(この障害がこのクライアントの接続に影響しないためです)。

HAイベント通知メカニズムにより、障害が発生した際のアプリケーションの応答時間が短縮されます。Oracle Database 10gリリース2 (10.2)でメカニズムが導入される以前は、障害が発生した場合、TCPタイムアウトの期限が切れるまで接続は切断されず、これには数分かかることがありました。HAイベント通知では、スタンドアロンおよびセッション・プール接続がOCIによって自動的に切断されクリーン・アップされ、障害イベントが発生してから数秒以内にアプリケーション・コールバックが呼び出されます。これらのサーバー・ハンドルのいずれかがTAFに対応している場合は、フェイルオーバーもOCIによって自動的に実行されます。

現在のリリースでは、この機能はOracle Notification Service (ONS)に応じて異なります。クライアントがONSを介してHA通知を受け取ることができるように、データベース・サーバー上にOracle Clusterwareをインストールして構成する必要があります。すべてのClusterwareインストール(たとえば、Oracle Data Guard)では、同じONSポートを指定する必要があります。ONSではクライアント構成は必要ありません。

ノート:

クライアントでは、接続されているデータベースからONSサーバー情報が透過的に取得されます。アプリケーションの管理者は、デプロイメント構成ファイルoraaccess.xmlを使用してこの情報を拡張するか、またはオーバーライドできます。

HAイベント通知を有効化するには、アプリケーションはOracle RACインスタンスに接続する必要があります。また、これらのアプリケーションで次を行う必要があります。

  • OCI_EVENTSモードでのOCI環境の初期化

  • 通知が有効化されたサービスへの接続(DBMS_SERVICE.MODIFY_SERVICEプロシージャを使用してAQ_HA_NOTIFICATIONSTRUEに設定する)

  • スレッド・ライブラリとのリンク

これにより、これらのアプリケーションはコールバックを登録でき、このコールバックが、HAイベントの発生のたびに呼び出されるようになります。

関連項目:

oraaccess.xmlの詳細は「oraaccess.xmlで指定されるクライアント側デプロイメント・パラメータについて」と、<events><fan>および<ons>のパラメータの詳細を参照してください

11.4.1.1 OCIEventハンドル

OCIEventハンドルは、イベント・ペイロードの属性をカプセル化します。

OCIでは、イベント・コールバックを呼び出す前にこのハンドルを暗黙的に割り当て、その結果イベント・コールバックでは、OCIAttrGet()を呼び出すことにより、イベントの読取り専用属性を取得できます。これらの属性に関連するメモリーが有効なのは、イベント・コールバック期間中のみです。

11.4.1.2 接続プールおよびセッション・プールのOCIフェイルオーバー

OCIセッション・プールにより、Oracle Databaseへの接続のコレクションが保持されます。Oracle RACがデプロイされている場合は、セッション・プールに、Oracle RACクラスタの様々なインスタンスへの接続が含まれている場合があります。

データベース・インスタンス障害の通知を受け取った場合は、特定のインスタンスに接続されているすべての接続をクリーン・アップする必要があります。接続が使用されている場合、OCIで接続を閉じる必要があります。これにより、即座に透過的アプリケーション・フェイルオーバー(TAF)が発生し、これらの接続が再確立されます。アイドル状態でプールの空きリストに入っている接続は消去する必要があるため、不適切な接続はプールからユーザーには戻されません。

カスタム接続プールに対応するために、OCIには、環境ハンドルに登録可能なコールバック関数が用意されています。登録すると、このコールバックはHAイベントが発生したときに呼び出されます。なお、OCIセッション・プールからのサーバー・ハンドルは、コールバックには渡されません。このため、場合によっては、空の接続リストを使用してコールバックが呼び出されることがあります。

11.4.1.3 独立した接続のOCIフェイルオーバー

独立した接続に対しては特別な処理は必要なく、障害の発生したインスタンスに接続されている接続はすべて即座に切断されます。

アイドル状態の接続に対しては、TAFにより、この接続が後続のOCIコールで使用された際に再確立されます。接続が失敗イベントで使用されている場合は即座に切断されるため、TAFを開始できます。これは、接続の「使用中」の接続およびセッション・プールにも当てはまります。

11.4.1.4 イベント・コールバック

OCIEventCallback型のイベント・コールバックのシグネチャを示します。

OCIEventCallback型のイベント・コールバックには、次のシグネチャがあります。

void evtcallback_fn (void      *evtctx,
                     OCIEvent  *eventhp );

このシグネチャでは、evtctxはクライアントのコンテキストで、OCIEventは、OCIライブラリに対しては不透明なイベント・ハンドルです。もう1つの入力引数は、イベント・ハンドルであるeventhpです(イベントに関連付けられている属性)。

この関数は、登録されると、イベントのたびに1回ずつ呼び出されます。Oracle RAC HAイベントの場合、このコールバックは、影響を受けた接続が切断されてから呼び出されます。次の環境ハンドル属性を使用して、イベントのコールバックとコンテキストをそれぞれ登録します。

  • OCI_ATTR_EVTCBKのデータ型はOCIEventCallback *です。これは読取り専用です。

  • OCI_ATTR_EVTCTXのデータ型はvoid *です。これも読取り専用です。

text *myctx = "dummy context"; /* dummy context passed to callback fn */
...
/* OCI_ATTR_EVTCBK and OCI_ATTR_EVTCTX are read-only. */
OCIAttrSet(envhp, (ub4) OCI_HTYPE_ENV, (void *) evtcallback_fn,
           (ub4) 0, (ub4) OCI_ATTR_EVTCBK, errhp);
OCIAttrSet(envhp, (ub4) OCI_HTYPE_ENV, (void *) myctx,
           (ub4) 0, (ub4) OCI_ATTR_EVTCTX, errhp);
...

OCIイベント・コールバック内では、影響を受けるサーバー・ハンドルのリストは、OCIEventハンドルにカプセル化されます。Oracle RAC HA DOWNイベントの場合は、属性タイプOCI_ATTR_HA_SRVFIRSTおよびOCI_ATTR_HA_SRVNEXTを指定してOCIAttrGet()を使用することで、クライアント・アプリケーションで、そのイベントの影響を受けるデータベース・サーバー・ハンドルのリストを反復できます:

OCIAttrGet(eventhp, OCI_HTYPE_EVENT, (void *)&srvhp, (ub4 *)0,
           OCI_ATTR_HA_SRVFIRST, errhp); 
/* or, */
OCIAttrGet(eventhp, OCI_HTYPE_EVENT, (void *)&srvhp, (ub4 *)0,
           OCI_ATTR_HA_SRVNEXT, errhp);

この関数は、属性OCI_ATTR_HA_SRVFIRSTを指定して呼び出されると、影響を受けるサーバー・ハンドルのリスト内の最初のサーバー・ハンドルを取得します。この関数は、属性OCI_ATTR_HA_SRVNEXTを使用して呼び出されると、リスト内の次のサーバー・ハンドルを取得します。戻すサーバー・ハンドルがなくなると、この関数はOCI_NO_DATAを戻し、srvhpNULLポインタになります。

srvhpは、HAイベントが原因で接続がクローズされたデータベース・サーバー・ハンドルへの出力ポインタです。errhpは、移入されるエラー・ハンドルです。アプリケーションは、取得する影響を受けたサーバー・ハンドルがなくなると、OCI_NO_DATAエラーを戻します。

HAイベントの影響を受けたサーバー・ハンドルのリストを取得する場合、接続はクローズ済でありかつ多くのサーバー・ハンドル属性が無効になっていることに注意してください。かわりに、イベント通知コールバックに必要な接続ごとの属性を、サーバー・ハンドルのユーザー・メモリー・セグメントに格納します。このメモリーは、サーバー・ハンドルが解放されるまで有効です。

関連項目:

OCIAttrGet()

11.4.1.5 カスタム・プーリング: タグ付けされたデータベース・サーバー・ハンドル

カスタム・プールを使用すると、データベース・サーバー・ハンドルのタグ情報を取得できるため、適切なクリーン・アップを実行できます。

次の機能は、カスタム・プールに適用されます。

  • データベース・サーバー・ハンドルは、カスタム・プールのかわりに作成されている場合は、それに、その親接続オブジェクトを使用してタグを付けることができます。OCIHandleAlloc()の"ユーザー・メモリー"パラメータを使用して、データベース・サーバー・ハンドルをユーザー・メモリー・セグメントに割り当てるよう要求します。ユーザー・メモリー・セグメントのポインタは、OCIHandleAlloc()によって戻されます。

  • HAイベントが発生し、影響を受けたサーバー・ハンドルが取得されていた場合は、適切なクリーン・アップを実行できるようデータベース・サーバー・ハンドルのタグ情報を取得する方法があります。OCI_ATTR_USER_MEMORY属性を使用して、ハンドルのユーザー・メモリー・セグメントのポインタを取得します。OCI_ATTR_USER_MEMORYは、ユーザーが割り当てたすべてのハンドルに対して有効です。ハンドルに追加メモリーが割り当てられている場合、ユーザー・メモリーへのポインタが戻されます。追加メモリーが割り当てられていないハンドルに関しては、NULLポインタが戻されます。この属性は読取り専用で、データ型はvoid*です。

ノート:

データベース・サーバー・ハンドルのユーザー・メモリー・セグメントの正確な内容は自由に定義できます。OCIではこのメモリーからは書込みも読取りも実行されないため、これにより、HAイベント・コールバック内からのクリーン・アップ・アクティビティ(必要な場合は、その他の目的)が容易になります。ユーザー・メモリー・セグメントは、サーバー・ハンドルに対するOCIHandleFree()のコールによって解放されます。

例11-3では、イベント通知の例を示しています。

例11-3 イベント通知

sword retval;
OCIServer *srvhp;
struct myctx {
   void *parentConn_myctx;
   uword numval_myctx;
};
typedef struct myctx myctx; 
myctx  *myctxp;
/* Allocate a server handle with user memory - pre 10.2 functionality */
if (retval = OCIHandleAlloc(envhp, (void **)&srvhp, OCI_HTYPE_SERVER,
                            (size_t)sizeof(myctx), (void **)&myctxp)
/* handle error */
myctxp->parentConn_myctx = <parent connection reference>;
 
/* In an event callback function, retrieve the pointer to the user memory */
evtcallback_fn(void *evtctx, OCIEvent *eventhp)
{ 
  myctx *ctxp = (myctx *)evtctx;
  OCIServer *srvhp;
  OCIError *errhp;
  sb4       retcode;
  retcode = OCIAttrGet(eventhp, OCI_HTYPE_SERVER, &srvhp, (ub4 *)0,
                       OCI_ATTR_HA_SRVFIRST, errhp); 
  while (!retcode) /* OCIAttrGet will return OCI_NO_DATA if no more srvhp */ 
  {  
     OCIAttrGet((void *)srvhp, OCI_HTYPE_SERVER, (void *)&ctxp,
                (ub4)0, (ub4)OCI_ATTR_USER_MEMORY, errhp);
           /* Remove the server handle from the parent connection object */
     retcode = OCIAttrGet(eventhp, OCI_HTYPE_SERVER, &srvhp, (ub4 *)0,
                          OCI_ATTR_HA_SRVNEXT, errhp);
...
  }
...
}
11.4.1.6 透過的アプリケーション・フェイルオーバー(TAF)機能の決定について

接続がTAFまたはACに対応している場合もしていない場合も、アプリケーションでその動作が調整されるようにすることができます。

次のようにOCIAttrGet()を使用して、サーバー・ハンドルがTAFまたはACに対応しているかどうかを確認します:

boolean taf_capable;
...
OCIAttrGet(srvhp, (ub4) OCI_HTYPE_SERVER, (void *) &taf_capable, 
           (ub4) sizeof(taf_capable), (ub4)OCI_ATTR_TAF_ENABLED, errhp);
...

この例では、taf_capableブール変数であるため、このコールでは、データベース・サーバー・ハンドルがTAFに対応している場合はTRUE、対応していない場合はFALSEに設定されます。srvhpは、入力ターゲットのデータベース・サーバー・ハンドルです。OCI_ATTR_TAF_ENABLEDは、ブール変数へのポインタであり読取り専用の属性です。errhpは、入力エラー・ハンドルです。

11.4.2 フェイルオーバー・コールバック

フェイルオーバー・コールバックとは、ユーザー定義関数です。

これはフェイルオーバー・イベントの間に自動的に実行されます。それにより、アプリケーションでデータベース接続での変更点に適切に対応できるようになります。フェイルオーバー・コールバックは、透過的アプリケーション・フェイルオーバー(TAF)およびAC/TACのメカニズムの一部です。それにより、アプリケーションで、データベース・インスタンスを使用できなくなったときに正常にリカバリできるようになります。

11.4.2.1 OCIでのフェイルオーバー・コールバック

フェイルオーバー中に遅延が発生する可能性があるため、アプリケーション開発者は、フェイルオーバーが進行中であることをユーザーに知らせ、フェイルオーバーの完了通知をユーザーが待機するよう要求した方がよい場合があります。

さらに、最初のインスタンスのセッションで、いくつかのALTER SESSIONコマンドを受け取る可能性もあります。これらのALTER SESSIONコマンドが、2番目のインスタンスで自動的に再実行されることはありません。したがって、開発者は、これらを2番目のインスタンスで再実行する可能性があります。セッションに影響を与えるOCIAttrSet()コールも再実行する必要があります。

これらの要件に対応するため、アプリケーションの開発者は、フェイルオーバー・コールバック関数を登録できます。フェイルオーバーが発生すると、コールバック関数が、ユーザーのセッションを再確立する間に数回呼び出されます。

コールバック関数が最初にコールされるのは、データベースがインスタンスの接続中断を最初に検出したときです。このコールバックは、アプリケーションがユーザーに対して遅延の発生を通知することを目的にしています。フェイルオーバーが正常終了すると、コールバック関数の2番目のコールは、接続が再確立して使用可能になったときに発生します。

接続が再確立されると、クライアントではALTER SESSIONコマンドを再実行し、ユーザーにフェイルオーバーが発生したことを通知できます。フェイルオーバーが失敗すると、フェイルオーバーが発生しなかったことをアプリケーションに通知するために、コールバックがコールされます。さらに、1次ハンドル以外のユーザー・ハンドルが新しい接続で再認証されるたびに、コールバックがコールされます。各ユーザー・ハンドルはデータベース層セッションを表すため、OCIアプリケーション層では、そのセッションに対してALTER SESSIONコマンドをリプレイできます。

関連項目:

11.4.2.2 フェイルオーバー・コールバック構造およびパラメータ

コールバック構造およびパラメータを示して説明します。

コールバック関数の基本的な構造は次のとおりです:

sb4  failovercbk_fn(OCISvcCtx *svchp, 
                    OCIEnv    *envhp, 
                    void      *fo_ctx, 
                    ub4        fo_type, 
                    ub4        fo_event);

次のパラメータの例は、「フェイルオーバー・コールバックの例」(9‐31ページ)にあります。

svchp

svchpは、サービス・コンテキスト・ハンドルです。これはvoid *型です。

envhp

envhpは、OCI環境ハンドルです。これはvoid *型です。

fo_ctx

fo_ctxは、クライアント・コンテキストです。この領域には、クライアントが必要な状態またはコンテキストを保管できます。このパラメータはvoid *型です。

fo_type

fo_typeは、フェイルオーバー・タイプです。このパラメータにより、クライアントが要求したフェイルオーバーの種類をコールバックが判断できます。通常の値は次のとおりです。

  • OCI_FO_SESSIONは、ユーザーがTAFセッション・フェイルオーバーを構成してあることを示します。

  • OCI_FO_SELECTは、ユーザーがTAF選択フェイルオーバーを構成してあることを示します。

  • OCI_FO_TRANSACTIONは、ユーザーがACを構成してあることを示します。

  • OCI_FO_AUTOは、ユーザーがTACを構成してあることを示します。

fo_event

fo_eventは、フェイルオーバー・イベントです。このパラメータは、コールバックがコールされた理由を示します。次の値をとることができます。

  • OCI_FO_BEGIN- フェイルオーバーが中断した接続を検出し、フェイルオーバーが開始することを示します。

  • OCI_FO_END- フェイルオーバーが正常終了したことを示します。

  • OCI_FO_ABORT- フェイルオーバーが失敗し、再試行できないことを示します。

  • OCI_FO_ERROR- 同様にフェイルオーバーが失敗したことを示しますが、アプリケーションでエラーを処理してフェイルオーバーを再試行する機会が与えられます。

  • OCI_FO_REAUTH- 複数の認証ハンドルがあり、最初の認証の後にフェイルオーバーが発生したことを示します。ユーザー・ハンドルが再認証されたことを示します。どのユーザー・ハンドルかを調べるために、アプリケーションで、サービス・コンテキスト・ハンドルsvchpOCI_ATTR_SESSION属性をチェックします。

アプリケーション・コンティニュイティが構成されている場合、TAFコールバックは、再接続、再認証、および進行中トランザクションのステータスの判別が正常に実行された後に、OCI_FO_ENDを指定してコールされます。TAFコールバックの完了時に、オープン・トランザクションが存在し、OCIのアプリケーション・コンティニュイティが有効になっている場合、OCIからエラーが戻されます。

11.4.2.3 フェイルオーバー・コールバックの登録

フェイルオーバー・コールバックを使用するには、それをサーバー・コンテキスト・ハンドルに登録する必要があります。この登録は、コールバック定義構造体を作成し、サーバー・ハンドルのOCI_ATTR_FOCBK属性にこの構造体を設定することにより行います。

コールバック定義構造の型はOCIFocbkStructにする必要があります。ここには、2つのフィールドがあり、フィールドcallback_functionにはコールする関数のアドレスが含まれており、フィールドfo_ctxにはクライアント・コンテキストのアドレスが含まれています。

関連項目:

コールバック登録の例については、例11-5を参照してください

11.4.2.4 フェイルオーバー・コールバックの例

フェイルオーバー・コールバックの例をいくつか示します。

この項では、簡単なユーザー定義コールバック関数の定義の例(例11-4を参照)、フェイルオーバー・コールバックの登録の例(例11-5を参照)およびフェイルオーバー・コールバックの登録解除の例(例11-6を参照)を示します。

例11-4 ユーザー定義フェイルオーバー・コールバック関数の定義

sb4  callback_fn(svchp, envhp, fo_ctx, fo_type, fo_event)
void * svchp;
void * envhp;
void *fo_ctx;
ub4 fo_type;
ub4 fo_event;
{
switch (fo_event) 
   {
   case OCI_FO_BEGIN:
   {
     printf(" Failing Over ... Please stand by \n");
     printf(" Failover type was found to be %s \n",
                     ((fo_type==OCI_FO_NONE) ? "NONE"
                     :(fo_type==OCI_FO_SESSION) ? "SESSION"
                     :(fo_type==OCI_FO_SELECT) ? "SELECT"
                     :(fo_type==OCI_FO_TXNAL) ? "TRANSACTION"
                     :(fo_type==OCI_FO_AUTO) ? “AUTO”
                     : "UNKNOWN!")); 
     printf(" Failover Context is :%s\n", 
                    (fo_ctx?(char *)fo_ctx:"NULL POINTER!"));
     break;
   }
   case OCI_FO_ABORT:
   {
     printf(" Failover stopped. Failover will not occur.\n");
     break;
   }
   case    OCI_FO_END:
   {
       printf(" Failover ended ...resuming services\n");
     break;
   }
   case OCI_FO_REAUTH:
   {
       printf(" Failed over user. Resuming services\n");
     break;
   }
   default:
   {
     printf("Bad Failover Event: %d.\n",  fo_event);
     break;
   }
   }
   return 0;
}

例11-5 フェイルオーバー・コールバックの登録

int register_callback(srvh, errh)
void *srvh; /* the server handle */
OCIError *errh; /* the error handle */
{
  OCIFocbkStruct failover;                 /*  failover callback structure */
  /* allocate memory for context */
  if (!(failover.fo_ctx = (void *)malloc(strlen("my context.")+1)))
     return(1);
  /* initialize the context. */
  strcpy((char *)failover.fo_ctx, "my context.");
  failover.callback_function = &callback_fn;
  /* do the registration */
  if (OCIAttrSet(srvh, (ub4) OCI_HTYPE_SERVER,
                (void *) &failover, (ub4) 0,
                (ub4) OCI_ATTR_FOCBK, errh)  != OCI_SUCCESS)
     return(2);
  /* successful conclusion */
  return (0);
}

例11-6 フェイルオーバー・コールバックの登録解除

OCIFocbkStruct failover;   /*  failover callback structure */
sword status;
 
  /* set the failover context to null */
  failover.fo_ctx = NULL; 
  /* set the failover callback to null */ 
  failover.callback_function = NULL; 
  /* unregister the callback */
  status = OCIAttrSet(srvhp, (ub4) OCI_HTYPE_SERVER,
                      (void *) &failover, (ub4) 0,
                      (ub4) OCI_ATTR_FOCBK, errhp);
11.4.2.5 OCI_FO_ERRORの処理

フェイルオーバーは、成功しないことがあります。失敗した場合は、コールバック関数には、fo_eventパラメータにOCI_FO_ABORTまたはOCI_FO_ERRORの値が戻されます。

OCI_FO_ABORTの場合は、フェイルオーバーが失敗し、フェイルオーバーを続行できないことを示します。ただし、OCI_FO_ERRORは、エラーを処理する機会のあるコールバック関数を提供します。たとえば、一定時間待機してから、コールバックからOCIライブラリにフェイルオーバーの再試行を指示する必要があります。

表11-1で提示されているイベントの時系列について考えます。

表11-1 時間およびイベント

時間 イベント

T0

データベースに障害が発生します(障害はT5まで続きます)。

T1

ユーザーの操作によってフェイルオーバーが動作します。

T2

ユーザーが再接続を試行します。試行が失敗します。

T3

OCI_FO_ERRORによってフェイルオーバー・コールバックがコールされます。

T4

フェイルオーバー・コールバックは事前に定義されたスリープ状態になります。

T5

データベースがリストアされます。

T6

フェイルオーバー・コールバックによって新しいフェイルオーバーが動作し、成功します。

T7

ユーザーは再接続に成功します。

コールバック関数からOCI_FO_RETRYの値が戻されると、新しいフェイルオーバーが動作します。

例11-7では、前述のシナリオと類似したフェイルオーバー手法の実装に使用できるコールバック関数を示しています。この場合、フェイルオーバー・コールバックはループに入り、スリープ状態になってから、成功するまでフェイルオーバーを再試行します。

例11-7 フェイルオーバー手法を実装するコールバック関数

/*--------------------------------------------------------------------*/
/* the user-defined failover callback  */
/*--------------------------------------------------------------------*/
sb4  callback_fn(svchp, envhp, fo_ctx, fo_type, fo_event )
void * svchp;
void * envhp;
void *fo_ctx;
ub4 fo_type;
ub4 fo_event;
{
   OCIError *errhp;
   OCIHandleAlloc(envhp, (void **)&errhp, (ub4) OCI_HTYPE_ERROR,
              (size_t) 0, (void **) 0);
   switch (fo_event) 
   {
   case OCI_FO_BEGIN:
   {
     printf(" Failing Over ... Please stand by \n");
     printf(" Failover type was found to be %s \n",
            ((fo_type==OCI_FO_NONE) ? "NONE"
             :(fo_type==OCI_FO_SESSION) ? "SESSION" 
             :(fo_type==OCI_FO_SELECT) ? "SELECT"
             :(fo_type==OCI_FO_TXNAL) ? "TRANSACTION"
             : "UNKNOWN!")); 
     printf(" Failover Context is :%s\n", 
            (fo_ctx?(char *)fo_ctx:"NULL POINTER!"));
     break;
   }
   case OCI_FO_ABORT:
   {
     printf(" Failover aborted. Failover will not occur.\n");
     break;
   }
   case    OCI_FO_END:
   { 
       printf("\n Failover ended ...resuming services\n");
     break;
   }
   case OCI_FO_REAUTH:
   { 
       printf(" Failed over user. Resuming services\n");
     break;
   }
   case OCI_FO_ERROR:
   {
     /* all invocations of this can only generate one line. The newline
      * will be put at fo_end time.
      */
     printf(" Failover error gotten. Sleeping...");
     sleep(3);
     printf("Retrying. ");
     return (OCI_FO_RETRY);
     break;
   }
   default:
   {
     printf("Bad Failover Event: %d.\n",  fo_event);
     break;
   }
   }
   return 0;
}