トランザクションの参加要素としてのリソースの参加

トランザクションに参加する各リソースは、リソース マネージャーによって管理され、その動作はトランザクション マネージャーによって調整されます。 この調整は、トランザクション マネージャーを介してトランザクションに参加したサブスクライバーへの通知によって行われます。

ここでは、単一または複数のリソースがトランザクションに参加する方法と、さまざまな種類の参加について説明します。 「フェーズおよび複数フェーズでのトランザクションのコミット」のトピックでは、参加リソース間でトランザクションのコミットメントを調整する方法について説明しています。

トランザクションへのリソースの参加

リソースをトランザクションに参加させるには、リソースをトランザクションに登録する必要があります。 Transaction クラスは、この機能を提供する一連のメソッドを定義しています。これらのメソッドの名前は Enlist で始まります。 さまざまな Enlist メソッドは、リソース マネージャーが持つ各種の参加リストにそれぞれ対応しています。 具体的には、揮発性リソースには EnlistVolatile メソッド、永続性リソースには EnlistDurable メソッドを使用します。 リソース マネージャーの永続性または揮発性とは、リソース マネージャーがエラーの回復をサポートするかどうかを意味します。 リソース マネージャーがエラーの回復をサポートする場合、フェーズ 1 (準備) 中にデータが永続ストレージに保存されます。したがって、リソース マネージャーがダウンした場合でも、回復時にトランザクションへの再参加を行い、TM から受信した通知に基づいて適切な動作を実行できます。 一般に、揮発性リソース マネージャーは、メモリ内のデータ構造 (たとえば、メモリ内のトランザクション ハッシュ テーブル) などの揮発性リソースを管理し、永続的リソース マネージャーは、より永続的なバッキング ストアを持つリソース (たとえば、バッキング ストアがディスクであるデータベース) を管理します。

簡単に説明すると、リソースが永続性をサポートするかどうかに応じて、EnlistDurable メソッドまたは EnlistVolatile メソッドのどちらを使用するかを決定した後、リソース マネージャーに IEnlistmentNotification インターフェイスを実装して、2 フェーズ コミット (2PC) に参加するようにリソースを登録する必要があります。 2PC の詳細については、「単一フェーズおよび複数フェーズでのトランザクションのコミット」を参照してください。

EnlistDurable および EnlistVolatile を複数回呼び出すことにより、これらのプロトコルのうち、複数のプロトコルに単一の参加要素を参加させることができます。

永続的参加リスト

EnlistDurable メソッドは、リソース マネージャーを永続性リソースとしてトランザクションに参加させるために使用します。 永続的リソース マネージャーがトランザクション中にダウンした場合、リソース マネージャーはオンラインに戻った後、フェーズ 2 が完了していない参加要素を Reenlist メソッドを使用してすべてのトランザクションに再参加させ、回復を実行します。回復プロセスが完了したら、RecoveryComplete を呼び出します。 回復の詳細については、「回復の実行」を参照してください。

すべての EnlistDurable メソッドは、最初のパラメーターとして Guid オブジェクトを使用します。 トランザクション マネージャーは、この Guid を使用して、永続参加リストと特定のリソース マネージャーを関連付けます。 そのため、リソース マネージャーが再起動時に自身を他のリソース マネージャーと識別できるように、一貫して同じ Guid を使用することが不可欠です。そうでない場合、回復が失敗する可能性があります。

EnlistDurable メソッドの第 2 パラメーターは、トランザクション通知を受信するためにリソース マネージャーが実装するオブジェクトへの参照です。 使用するオーバーロードにより、リソース マネージャーが単一フェーズ コミット (SPC) の最適化をサポートするかどうかがトランザクション マネージャーに通知されます。 ほとんどの場合、2 フェーズ コミット (2PC) に参加するために IEnlistmentNotification インターフェイスを実装します。 ただし、コミット プロセスを最適化する場合は、SPC 用の ISinglePhaseNotification インターフェイスの実装を検討することもできます。 SPC の詳細については、「フェーズおよび複数フェーズでのトランザクションのコミット」および「単一フェーズ コミットおよび昇格可能単一フェーズ通知を使用した最適化」を参照してください。

第 3 パラメーターは EnlistmentOptions 列挙体であり、その値は None または EnlistDuringPrepareRequired のいずれかです。 この値が EnlistDuringPrepareRequired に設定されている場合、トランザクション マネージャーから Prepare 通知を受信したときに、参加リストが追加リソース マネージャーを参加させる可能性があります。 ただし、この種類の参加リストは単一フェーズ コミットの最適化の条件を満たさないことに注意してください。

揮発性参加リスト

キャッシュなどの揮発性リソースを管理する参加要素は、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 (Promotable Single Phase Enlistment) を参加させるための EnlistPromotableSinglePhase メソッドも提供しています。 これにより、永続的リソース マネージャー (RM) は、MSDTC による管理のために後で必要に応じてエスカレートできるトランザクションをホストおよび "所有" できます。 詳細については、「単一フェーズ コミットおよび昇格可能単一フェーズ通知を使用した最適化」を参照してください。

関連項目