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

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

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

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

リソースをトランザクションに参加させるには、リソースをトランザクションに登録する必要があります。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 つに、トランザクションの不要なエスカレーションを強制しないことがあります。トランザクションのエスカレーションの詳細については、「トランザクション管理のエスカレーション」を参照してください。揮発的な参加を行うことは、トランザクション マネージャによる参加リストの処理方法、およびトランザクション マネージャがリソース マネージャに期待することの両方に相違が生じることを意味します。これは、揮発性リソース マネージャが回復を実行しないためです。揮発性リソース マネージャが回復を実行せず、Guid を必要とする Reenlist メソッドを呼び出さないため、EnlistVolatile メソッドは Guid パラメータを取得しません。

永続的参加リストの場合と同様に、どのオーバーロード メソッドを使用して参加する場合でも、リソース マネージャが単一フェーズ コミットの最適化をサポートするかどうかがトランザクション マネージャに示されます。揮発性リソース マネージャが回復を実行できないため、準備フェーズ中、回復情報は揮発性参加リストに書き込まれません。したがって、RecoveryInformation メソッドを呼び出すと、InvalidOperationException になります。

次の例は、EnlistVolatile メソッドを使用して、このようなオブジェクトを参加要素としてトランザクションに参加させる方法を示しています。

    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 indout notification is received

        'Declare done on the enlistment
        myEnlistment.Done()
    End Sub
End Class
static void Main(string[] args)
{
    try
    {
        using (TransactionScope scope = new TransactionScope())
        {
        
            //Create an enlistment object
            myEnlistmentClass myElistment = new myEnlistmentClass();

            //Enlist on the current transaction with the enlistment object
            Transaction.Current.EnlistVolatile(myElistment, 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 indout notification is received
        
        //Declare done on the enlistment
        enlistment.Done();
    }
}

パフォーマンスの最適化

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

関連項目

概念

単一フェーズ コミットおよび昇格可能単一フェーズ通知を使用した最適化
単一フェースおよび複数フェーズでのトランザクションのコミット

Footer image

Copyright © 2007 by Microsoft Corporation.All rights reserved.