単一フェースおよび複数フェーズでのトランザクションのコミット

トランザクションで使用される各リソースは、リソース マネージャ (RM) によって管理され、その動作はトランザクション マネージャ (TM) によって調整されます。「トランザクションの参加要素としてのリソースの参加」では、単一 (または複数) のリソースがトランザクションに参加する方法について説明しています。ここでは、参加リソース間でトランザクションのコミットメントを調整する方法について説明します。

トランザクションの最後に、アプリケーションはトランザクションのコミットまたはロールバックを要求します。トランザクション マネージャは、一部のリソース マネージャがトランザクションのコミットを選択し、他のリソース マネージャがトランザクションのロールバックを選択するというようなリスクを回避する必要があります。

トランザクションに複数のリソースが含まれる場合、2 フェーズ コミット (2PC) を実行する必要があります。2 フェーズ コミット プロトコル (準備フェーズとコミット フェーズ) により、トランザクションの終了時、すべてのリソースに対する変更が完全にコミットされるか、または完全にロールバックされることが保証されます。その後、すべての参加要素に最終結果が通知されます。2 フェーズ コミット プロトコルの詳細については、Jim Gray 著『Transaction Processing : Concepts and Techniques (Morgan Kaufmann Series in Data Management Systems) ISBN:1558601902』を参照してください。**

単一フェーズ コミット プロトコルに参加することで、トランザクションのパフォーマンスを最適化することもできます。詳細については、「単一フェーズ コミットおよび昇格可能単一フェーズ通知を使用した最適化」を参照してください。

トランザクション結果の通知を受信するだけで、コミットまたはロールバックの選択には参加しない場合は、TransactionCompleted イベントに登録する必要があります。

2 フェーズ コミット (2PC)

最初のトランザクション フェーズで、トランザクション マネージャは、トランザクションをコミットするかロールバックするかを決定するため、各リソースに照会します。2 番目のトランザクション フェーズでは、トランザクション マネージャは照会結果を各リソースに通知し、必要なクリーンアップを実行できるようにします。

この種のトランザクションに参加するため、リソース マネージャは IEnlistmentNotification インターフェイスを実装する必要があります。このインターフェイスは、2PC 中に通知として TM が呼び出すメソッドを提供します。このような実装の例を次に示します。

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
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();
    }
}

準備フェーズ (フェーズ 1)

アプリケーションから Commit 要求を受信すると、トランザクション マネージャは、各リソースのトランザクションに対する選択を取得するため、各参加リソースに対して Prepare メソッドを呼び出し、参加しているすべての参加要素の準備フェーズを開始します。

IEnlistmentNotification インターフェイスを実装するリソース マネージャは、次の簡単な例に示すように、最初に Prepare メソッドを実装する必要があります。

public void Prepare(PreparingEnlistment preparingEnlistment)
{
     Console.WriteLine("Prepare notification received");
     //Perform work

     Console.Write("reply with prepared? [Y|N] ");
     c = Console.ReadKey();
     Console.WriteLine();
 
     //If work finished correctly, reply with prepared
     if ((c.KeyChar == 'Y') || (c.KeyChar == 'y'))
     {
          preparingEnlistment.Prepared();
          break;
     }

     // otherwise, do a ForceRollback
     else if ((c.KeyChar == 'N') || (c.KeyChar == 'n'))
     {
          preparingEnlistment.ForceRollback();
          break;
     }
}

永続的リソース マネージャがこの呼び出しを受信すると、トランザクションの回復情報 (RecoveryInformation プロパティの取得により利用可) と、コミット時にトランザクションを完了させるのに必要な情報をすべてログに記録する必要があります。RM はワーカー スレッドでこれを実行できるため、Prepare メソッド内で実行する必要はありません。

この準備操作が完了したら、RM は Prepared メソッドまたは ForceRollback メソッドを呼び出して、コミットまたはロールバックを選択する必要があります。PreparingEnlistment クラスは、Enlistment クラスから Done メソッドを継承することに注意してください。準備フェーズ中に PreparingEnlistment コールバックでこのメソッドを呼び出すと、読み取り専用の参加 (トランザクション保護されたデータを読み取れるが更新できないリソース マネージャ) であることが TM に通知され、RM はフェーズ 2 でのトランザクションの結果として、トランザクション マネージャからの通知を受信しなくなります。

すべてのリソース マネージャが Prepared を選択すると、トランザクションのコミットメントが成功したことがアプリケーションに通知されます。

コミット フェーズ (フェーズ 2)

トランザクションの 2 番目のフェーズで、トランザクション マネージャがすべてのリソース マネージャから準備の成功を受信すると (すべてのリソース マネージャがフェーズ 1 の終わりで Prepared を呼び出した場合)、各リソース マネージャに対して Commit メソッドを呼び出します。これにより、リソース マネージャは、変更を永続的なものとして、コミットを完了できます。

いずれかのリソース マネージャがフェーズ 1 での準備の失敗を報告すると、トランザクション マネージャは、各リソース マネージャに対して Rollback メソッドを呼び出し、アプリケーションにコミットの失敗を通知します。

したがって、リソース マネージャは次のメソッドを実装する必要があります。

public void Commit (Enlistment enlistment)
{
     // Do any work necessary when commit notification is received

     // Declare done on the enlistment
     enlistment.Done();
}

public void Rollback (Enlistment enlistment)
{
     // Do any work necessary when rollback notification is received

     // Declare done on the enlistment  
     enlistment.Done();  
}

RM は、通知の種類に基づいてトランザクションを終了するために必要な作業を実行し、Enlistment パラメータで Done メソッドを呼び出すことにより、TM に作業の完了を通知する必要があります。これはワーカー スレッドで実行できます。フェーズ 2 の通知は、フェーズ 1 で Prepared メソッドを呼び出したのと同じスレッドで、インラインで発生する可能性があることに注意してください。このため、フェーズ 2 の通知を受け取る前に既に完了したと考えられる作業 (ロックの解除など) を Prepared 呼び出しの後で行うことはできません。

InDoubt の実装

最後に、揮発性リソース マネージャに InDoubt メソッドを実装する必要があります。このメソッドは、トランザクション マネージャが 1 つ以上の参加要素と接続できなくなり、そのステータスが不明になった場合に呼び出されます。この場合は、トランザクションのいずれかの参加要素が矛盾した状態のままになっていないかどうかを後で調べることができるよう、この事実を記録しておく必要があります。

public void InDoubt (Enlistment enlistment)
{
     // log this
     enlistment.Done();
}

単一フェーズ コミットの最適化

単一フェーズ コミット プロトコルは、すべての更新が明示的な調整なしに行われるため、実行時に、より効率的です。このプロトコルの詳細については、「単一フェーズ コミットおよび昇格可能単一フェーズ通知を使用した最適化」を参照してください。

関連項目

概念

単一フェーズ コミットおよび昇格可能単一フェーズ通知を使用した最適化
トランザクションの参加要素としてのリソースの参加

Footer image

Copyright © 2007 by Microsoft Corporation.All rights reserved.