トランザクションに参加する各リソースはリソース マネージャーによって管理され、そのアクションはトランザクション マネージャーによって調整されます。 調整は、トランザクション マネージャーを介してトランザクションに参加したサブスクライバーに通知を通じて行われます。
このトピックでは、1 つのトランザクションにリソース (または複数のリソース) を参加させる方法と、さまざまな種類の参加リストについて説明します。 「 Single-Phase とマルチフェーズでのトランザクションのコミット 」トピックでは、参加しているリソース間でトランザクション コミットメントを調整する方法について説明します。
トランザクションにリソースを参加させる
リソースをトランザクションに参加させるには、リソースがトランザクションに参加する必要があります。 Transaction クラスは、この機能を提供する Enlist で始まる名前を持つ一連のメソッドを定義します。 さまざまな Enlist メソッドは、リソース マネージャーが持つ可能性のあるさまざまな種類の参加リストに対応します。 具体的には、揮発性リソースには EnlistVolatile メソッドを使用し、永続リソースには EnlistDurable メソッドを使用します。 リソース マネージャーの持続性 (逆にボラティリティ) とは、リソース マネージャーが障害復旧をサポートしているかどうかを指します。 リソース マネージャーが障害復旧をサポートしている場合、リソース マネージャーはフェーズ 1 (準備) 中に永続ストレージにデータを保持します。これにより、リソース マネージャーがダウンした場合は、復旧時にトランザクションに再参加し、TM から受信した通知に基づいて適切なアクションを実行できます。 一般に、揮発性リソース マネージャーはメモリ内データ構造 (メモリ内トランザクション ハッシュテーブルなど) などの揮発性リソースを管理し、永続的なリソース マネージャーは、より永続的なバッキング ストア (たとえば、バッキング ストアがディスクであるデータベース) を持つリソースを管理します。
わかりやすくするために、リソースの持続性のサポートに基づいて EnlistDurable または EnlistVolatile のどちらの方法を使用するかを決定した後、リソース マネージャーの IEnlistmentNotification インターフェイスを実装して、リソースを 2 フェーズ コミット (2PC) に参加させる必要があります。 2PC の詳細については、「 Single-Phase およびマルチフェーズでのトランザクションのコミット」を参照してください。
1 人の参加者は、EnlistDurable と EnlistVolatile を複数回呼び出すことで、これらのプロトコルのいくつかに登録できます。
永続的参加リスト
EnlistDurableメソッドは、リソース マネージャーを永続リソースとしてトランザクションに参加させるために使用されます。 永続リソース マネージャーがトランザクション中に強制終了された場合、参加してフェーズ2を完了していないすべてのトランザクションにReenlistメソッドを使用して再参加し、オンラインに戻った後に回復を実行することが期待されます。復旧処理が完了したらRecoveryCompleteを呼び出します。 回復の詳細については、「回復 の実行」を参照してください。
EnlistDurable メソッドはすべて、最初のパラメーターとして Guid オブジェクトを受け取ります。 Guidは、永続的な参加リストを特定のリソース マネージャーに関連付けるためにトランザクション マネージャーによって使用されます。 そのため、リソース マネージャーは、再起動時に異なるリソース マネージャー間でも同じ Guid を一貫して使用して自身を識別することが不可欠です。そうしないと、復旧が失敗する可能性があります。
EnlistDurable メソッドの 2 番目のパラメーターは、トランザクション通知を受信するためにリソース マネージャーが実装するオブジェクトへの参照です。 使用するオーバーロードは、リソース マネージャーが単一フェーズ コミット (SPC) の最適化をサポートしているかどうかをトランザクション マネージャーに通知します。 ほとんどの場合、 IEnlistmentNotification インターフェイスを実装して、Two-Phase Commit (2PC) に参加します。 ただし、コミット プロセスを最適化する場合は、SPC の ISinglePhaseNotification インターフェイスの実装を検討できます。 SPC の詳細については、「Single-Phase におけるトランザクションのコミットと複数フェーズでのコミット」および「単一フェーズコミットと昇格可能な単一フェーズ通知を使用した最適化」を参照してください。
3 番目のパラメーターは EnlistmentOptions 列挙体であり、値は None または EnlistDuringPrepareRequiredにすることができます。 値が EnlistDuringPrepareRequired に設定されている場合、参加リストは、トランザクション マネージャーから準備通知を受信したときに、追加のリソース マネージャーを参加させる可能性があります。 ただし、この種類の参加リストは、単一フェーズ コミットの最適化の対象ではないことに注意する必要があります。
不安定な入隊
キャッシュなどの揮発性リソースを管理する参加者は、 EnlistVolatile メソッドを使用して参加する必要があります。 このようなオブジェクトは、トランザクションの結果を取得したり、システム障害の後に参加しているトランザクションの状態を回復したりできない場合があります。
前述のように、リソース マネージャーはメモリ内の揮発性リソースを管理する場合、揮発性の参加リストを作成します。 EnlistVolatileを使用する利点の 1 つは、トランザクションの不必要なエスカレーションを強制しないことです。 トランザクションエスカレーションの詳細については、「 トランザクション管理エスカレーション」 トピックを参照してください。 ボラティリティを登録することは、トランザクション マネージャーによる参加の処理方法と、トランザクション マネージャーがリソース マネージャーに期待する内容の両方の違いを意味します。 これは、揮発性リソース マネージャーが復旧を実行しないためです。 揮発性リソース マネージャーは復旧を実行せず、EnlistVolatileを必要とするGuid メソッドを呼び出さないため、Reenlist メソッドは Guid パラメーターを受け取りません。
永続的な参加リストと同様に、参加に使用するオーバーロード メソッドは、リソース マネージャーが単一フェーズ コミットの最適化をサポートしているかどうかをトランザクション マネージャーに示します。 揮発性リソース マネージャーは回復を実行できないため、準備フェーズ中に揮発性参加リストの回復情報は書き込まれなくなります。 したがって、 RecoveryInformation メソッドを呼び出すと、 InvalidOperationExceptionが発生します。
次の例は、 EnlistVolatile メソッドを使用して、このようなオブジェクトをトランザクションに参加させる方法を示しています。
static void Main(string[] args)
{
try
{
using (TransactionScope scope = new TransactionScope())
{
//Create an enlistment object
myEnlistmentClass myEnlistment = new myEnlistmentClass();
//Enlist on the current transaction with the enlistment object
Transaction.Current.EnlistVolatile(myEnlistment, EnlistmentOptions.None);
//Perform transactional work here.
//Call complete on the TransactionScope based on console input
ConsoleKeyInfo c;
while(true)
{
Console.Write("Complete the transaction scope? [Y|N] ");
c = Console.ReadKey();
Console.WriteLine();
if ((c.KeyChar == 'Y') || (c.KeyChar == 'y'))
{
scope.Complete();
break;
}
else if ((c.KeyChar == 'N') || (c.KeyChar == 'n'))
{
break;
}
}
}
}
catch (System.Transactions.TransactionException ex)
{
Console.WriteLine(ex);
}
catch
{
Console.WriteLine("Cannot complete transaction");
throw;
}
}
class myEnlistmentClass : IEnlistmentNotification
{
public void Prepare(PreparingEnlistment preparingEnlistment)
{
Console.WriteLine("Prepare notification received");
//Perform transactional work
//If work finished correctly, reply prepared
preparingEnlistment.Prepared();
// otherwise, do a ForceRollback
preparingEnlistment.ForceRollback();
}
public void Commit(Enlistment enlistment)
{
Console.WriteLine("Commit notification received");
//Do any work necessary when commit notification is received
//Declare done on the enlistment
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
Console.WriteLine("Rollback notification received");
//Do any work necessary when rollback notification is received
//Declare done on the enlistment
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
Console.WriteLine("In doubt notification received");
//Do any work necessary when in doubt notification is received
//Declare done on the enlistment
enlistment.Done();
}
}
Public Shared Sub Main()
Try
Using scope As TransactionScope = New TransactionScope()
'Create an enlistment object
Dim myEnlistmentClass As New EnlistmentClass
'Enlist on the current transaction with the enlistment object
Transaction.Current.EnlistVolatile(myEnlistmentClass, EnlistmentOptions.None)
'Perform transactional work here.
'Call complete on the TransactionScope based on console input
Dim c As ConsoleKeyInfo
While (True)
Console.Write("Complete the transaction scope? [Y|N] ")
c = Console.ReadKey()
Console.WriteLine()
If (c.KeyChar = "Y") Or (c.KeyChar = "y") Then
scope.Complete()
Exit While
ElseIf ((c.KeyChar = "N") Or (c.KeyChar = "n")) Then
Exit While
End If
End While
End Using
Catch ex As TransactionException
Console.WriteLine(ex)
Catch
Console.WriteLine("Cannot complete transaction")
Throw
End Try
End Sub
End Class
Public Class EnlistmentClass
Implements IEnlistmentNotification
Public Sub Prepare(ByVal myPreparingEnlistment As PreparingEnlistment) Implements System.Transactions.IEnlistmentNotification.Prepare
Console.WriteLine("Prepare notification received")
'Perform transactional work
'If work finished correctly, reply with prepared
myPreparingEnlistment.Prepared()
End Sub
Public Sub Commit(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.Commit
Console.WriteLine("Commit notification received")
'Do any work necessary when commit notification is received
'Declare done on the enlistment
myEnlistment.Done()
End Sub
Public Sub Rollback(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.Rollback
Console.WriteLine("Rollback notification received")
'Do any work necessary when rollback notification is received
'Declare done on the enlistment
myEnlistment.Done()
End Sub
Public Sub InDoubt(ByVal myEnlistment As Enlistment) Implements System.Transactions.IEnlistmentNotification.InDoubt
Console.WriteLine("In doubt notification received")
'Do any work necessary when in doubt notification is received
'Declare done on the enlistment
myEnlistment.Done()
End Sub
End Class
パフォーマンスの最適化
Transaction クラスは、プロモータブル単一フェーズ登録 (PSPE) を登録するためのEnlistPromotableSinglePhaseメソッドも提供しています。 これにより、永続リソース マネージャー (RM) は、必要に応じて MSDTC によって管理されるように後でエスカレートできるトランザクションをホストし、"所有" することができます。 この詳細については、「 単一フェーズ コミットと昇格可能な単一フェーズ通知を使用した最適化」を参照してください。
こちらも参照ください
.NET