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
インスタンス付きで作成されます。OracleLogicalTransaction
は、必ずnull以外になります。データベースでは、管理者によって指定された保存期間、論理トランザクションの結果が維持されます。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.LogicalTransactionId
プロパティこの値によって戻される値は、次の場合にはNULL以外になります
-
トランザクション・ガードが有効な場合、
-
接続において、透過的アプリケーション・フェイルオーバー(TAF)が無効な場合、および
-
ODP.NETがトランザクションの結果を判断できない場合。
指定された
OracleException
オブジェクトでは、OracleLogicalTransaction.Committed
およびOracleLogicalTransaction.UserCallCompleted
がすべてNULLを戻す場合、エラーは、リカバリ不能なエラー、トランザクション・ガードが無効または接続においてTAFが有効になっているかです。いずれの場合でも、アプリケーションは、ロールバックしてからトランザクションを再送信する必要があります。OracleLogicalTransaction.Committed
がtrueまたはfalseの場合、LogicalTransactionId
プロパティは、トランザクション・ガードが有効で、リカバリ可能なエラーが発生する場合でも、NULLを戻します。 -
-
OracleException.OracleLogicalTransaction.Committed
プロパティこのプロパティは、トランザクションがコミットされたかどうかに応じて、trueまたはfalseを戻します。次の場合に、NULLを戻します。
-
ODP.NETはトランザクションの結果を判断できなかった、
-
エラーはリカバリ不能、
-
トランザクション・ガードが無効、または
-
接続において、TAFが有効。
-
-
OracleException.OracleLogicalTransaction.UserCallCompleted
プロパティこのプロパティは、ユーザー・コールが完了している場合にはtrueを戻し、完了していない場合にはfalseを戻します。このプロパティは、次の場合にNULLを戻します
-
トランザクション・ガードが無効な場合、
-
エラーはリカバリ不能、
-
接続において、TAFが有効な場合、または
-
ODP.NETが、ユーザー・コールが完了しているかどうかを判断できない場合。
Committed
およびUserCallCompleted
の値が何を意味するかについては、次の表を参照してください。 -
表3-6 Committedの値とUserCallCompletedの値の意味
Committedの値 | UserCallCompletedの値 | 結果 |
---|---|---|
|
|
トランザクションは正常に完了しました。結果をアプリケーションに戻すことができます。 |
|
|
トランザクションは正常に完了していません。そのトランザクションをアプリケーションで再送信することができます。 |
|
|
トランザクションはコミットされましたが、アプリケーションで期待どおりに続行されていない未完了の状態(行数やネスト済のPL/SQLロジックなど)が含まれている可能性があります。 |
例3-4 トランザクション・ガードの使用: サンプル・コード
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.Committed == false)
{
// 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
}
}
}
サービスに対する透過的アプリケーション・フェイルオーバー(TAF)が有効になっている場合、トランザクション・ガードはサポートされません。したがって、TAFが有効な場合、LogicalTransactionId
は常にNULLになり、トランザクション結果では判断されません。
関連項目:
-
トランザクション・ガードの詳細は、『Oracle Database開発ガイド』を参照