3.5 論理破損を防ぐためのトランザクション・ガードの使用
トランザクション・ガードを使用すると、計画済および計画外の停止時や送信が繰り返された場合に、管理対象および管理対象外ODP.NETアプリケーションで実行を1回以下にすることができます。トランザクション・ガードを使用しないと、アプリケーションが停止の後に操作を実行しようとして、トランザクションが重複してコミットされ論理破損が発生する可能性があります。
停止後にアプリケーションをリカバリする際の従来の問題の1つは、継続性のないコミット・メッセージがクライアントに戻されることでした。クライアントとサーバーの間が中断されると、クライアントには通信が失敗したことを示す(リカバリ可能エラーとも呼ばれる)エラー・メッセージが示されます。このエラーは、送信でコミット操作が実行されたかどうかや、すべての予測されるコミットの実行中に完了まで手続きコールを実行したかどうかをアプリケーションに通知しません。エラーはセッション・ステート変更または断続的な障害も示しません。トランザクションがコミットされたかどうか、完全に完了したかどうか、クライアントは確認できないままです。
このリカバリ可能エラーでは、重複したトランザクションの送信またはその他の論理破損の形式で、エンド・ユーザーやアプリケーションがリプレイを試すことが必要です。トランザクションは、非トランザクション状態が不正確な場合、またはそのトランザクションがコミットされている場合は、有効に再送信できません。コミットされているが完了していないコールの処理を続けると、誤った状態のデータベース・セッションがアプリケーションで使用されることになります。
3.5.1 ODP.NETおよびトランザクション・ガード
トランザクション・ガードでは、ODP.NET管理対象ドライバおよびODP.NET管理対象外ドライバによって自動的かつ透過的に基準化された方法で、トランザクションの重複を解消します。
ノード、ネットワーク、データベースなどで障害が発生した場合、ODP.NETアプリケーションはデータベース・サービスが起動されていればステータスを問い合せることで、そのトランザクションがコミットされたかどうかを判断できます。これらの1つが失敗した後でも、Oracleは自動的にトランザクション・ステータスを保持します。
ODAC 12cリリース4では、トランザクション・ガード・アプリケーション開発の使用は合理化され、トランザクション結果を決定するために必要なアプリケーション・ロジックが削減されました。さらに、これらの利点は管理対象ODP.NETと管理対象外ODP.NETの両方に有効です。
トランザクション・ガードが有効なデータベース・サービスで、データベース・コミットまたはコミットをコールした可能性のあるSQLかPL/SQLの実行でリカバリ可能エラーが発生した場合、ODP.NET OracleException
がOracleLogicalTransaction
インスタンス付きで作成されます。データベースでは、管理者によって指定された保存期間、論理トランザクションの結果が維持されます。ODP.NETは、リカバリ可能なエラーが発生した際、アプリケーションのために自動でデータベースに問い合せます。これにより、OracleException
オブジェクトのOracleLogicalTransaction
オブジェクト・インスタンスは、トランザクションがコミットされているかどうか、およびユーザー・コールが完了しているかどうかを示すことができます。
ステータスがコミット済の場合、トランザクションは正常に完了しています。管理者が他の操作を行う必要はありません。
コミットされていない場合、ODP.NETアプリケーションは現在のトランザクション状態が、リカバリ可能かどうか、OracleLogicalTransaction
を使用して再試行可能かどうかを確認します。エラーがリカバリ可能な場合、トランザクションは安全に再送信できます。エラーがリカバリ不可の場合、アプリケーションは別のメカニズムを使用してトランザクション結果を判断する必要があります。
注意:
トランザクション・ガードは、ローカル・トランザクションのみをサポートしています。分散トランザクションはサポートしていません。
トランザクション・ガードの機能は、Oracleサービスレベル構成のCOMMIT_OUTCOME
設定によって有効または無効にします。デフォルトでは無効です。この設定はデータベースを停止せずに変更できます。新しい設定は、このサービスに対して作成された新規接続にのみ適用されます。
次に、SRVCTL
を使用してCOMMIT_OUTCOME
を設定する例を示します。
srvctl modify service -d orcl -s GOLD -commit_outcome TRUE
注意:
トランザクションのステータスを取得するデータベース・ユーザーには、DBMS_APP_CONT
パッケージのEXECUTE
権限を付与する必要があります。
GRANT EXECUTE ON DBMS_APP_CONT TO <user name> ;
次に、ODP.NETトランザクション・ガードのアプリケーション・シナリオの例を示します。
ODP.NETアプリケーションは、高速アプリケーション通知(FAN)ダウン・イベントまたはエラーを受信します。FANは、中断されたセッションを自動的に強制終了し、アプリケーションはOracleException
を受信します。エラーを透過的に処理するために作成されたトランザクション・ガード・アプリケーションは、次のことを実行します。
-
OracleException.OracleLogicalTransaction
プロパティの値を確認します。値がOracleLogicalTransaction
オブジェクトの場合(つまりnull以外)、エラーはリカバリ可能です。プロパティの値がnullの場合、エラーはリカバリ可能ではなく、トランザクション・ガードは有効化されていません。 -
リカバリ可能なエラーでは、
OracleLogicalTransaction.Committed
プロパティを確認します。true
の場合、トランザクションはコミットされています。false
の場合、トランザクションは送信されていませんが、安全に再送信できるようになりました。 -
リカバリ可能なエラーでは、コミット操作以外のトランザクションの状態が重要な場合、
OracleLogicalTransaction.UserCallCompleted
プロパティを確認します。Committed
およびUserCallCompleted
の値が何を意味するかについては、次の表を参照してください。
表3-6 Committedの値とUserCallCompletedの値の意味
Committedの値 | UserCallCompletedの値 | 結果 |
---|---|---|
|
|
トランザクションは正常に完了しました。結果をアプリケーションに戻すことができます。 |
|
|
トランザクションは正常に完了していません。そのトランザクションをアプリケーションで再送信することができます。 |
|
|
トランザクションはコミットされましたが、アプリケーションで期待どおりに続行されていない未完了の状態(行数やネスト済のPL/SQLロジックなど)が含まれている可能性があります。 |
サンプル・コード
using System; using Oracle.DataAccess.Client; //alternatively can use using Oracle.ManagedDataAccess.Client; class TransactionGuardSample { static void Main() { bool bReadyToCommit = false; string constr = "user id=hr;password=hr;data source=oracle"; OracleConnection con = new OracleConnection(constr); OracleTransaction txn = null; OracleCommand cmd = null; try { string sql = " update employees set salary=10000 where employee_id=103"; con.Open(); txn = con.BeginTransaction(); cmd = new OracleCommand(con, sql); cmd.ExecuteNonQuery(); bReadyToCommit = true; } catch (Exception ex) { // rollback here as the SQL execution is unsuccessful txn.Rollback(); Console.WriteLine(ex.ToString()); } try { if (bReadyToCommit) txn.Commit(); } catch (Exception ex) { if (ex is OracleException) { // It's safe to re-submit the work if the error is recoverable and the transaction has not been committed if (ex.IsRecoverable && ex.OracleLogicalTransaction != null && !ex.OracleLogicalTransaction.Committed) { // safe to re-submit work } else { // do not re-submit work } } } finally { // dispose all objects txn.Dispose(); cmd.Dispose(); con.Dispose(); // place the connection back to the connection pool } } }
関連項目:
-
トランザクション・ガードの詳細は、『Oracle Database開発ガイド』を参照