次の方法で共有


Single-Phase とマルチフェーズでのトランザクションのコミット

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

トランザクションの終了時に、アプリケーションがトランザクションのコミットまたはロールバックを要求します。 トランザクション マネージャーは、一部のリソース マネージャーがコミットに投票し、他のリソース マネージャーがトランザクションをロールバックするために投票するようなリスクを排除する必要があります。

トランザクションに複数のリソースが含まれる場合は、2 フェーズ コミット (2PC) を実行する必要があります。 2 フェーズのコミット プロトコル (準備フェーズとコミット フェーズ) により、トランザクションが終了すると、すべてのリソースに対するすべての変更が完全にコミットされるか、完全にロールバックされます。 その後、すべての参加者に最終結果が通知されます。 2 フェーズ コミット プロトコルの詳細については、Jim Gray の書籍「トランザクション処理 : 概念と手法 (データ管理システムのモルガン カウフマン シリーズ) ISBN:1558601902」を参照してください。

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

トランザクションの結果を知らせるだけで、投票に参加したくない場合は、 TransactionCompleted イベントに登録する必要があります。

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

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

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

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 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

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

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

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

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

Durable Resource Manager は、この呼び出しを受け取ると、トランザクションの復旧情報 ( RecoveryInformation プロパティを取得することによって使用可能) と、コミット時にトランザクションを完了するために必要な情報をログに記録する必要があります。 RM はワーカー スレッドでこれを行うことができるため、 Prepare メソッド内でこれを実行する必要はありません。

RM が準備作業を完了したら、 Prepared または ForceRollback メソッドを呼び出してコミットまたはロールバックすることに投票する必要があります。 PreparingEnlistment クラスは、Enlistment クラスからDone メソッドを継承していることに注意してください。 準備フェーズ中に PreparingEnlistment コールバックでこのメソッドを呼び出すと、TM に Read-Only 参加 (つまり、トランザクションで保護されたデータを読み取ることができるが更新できないリソース マネージャー) であることが通知され、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();  
}  

単一段階コミットの最適化

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

こちらも参照ください