Однофазная и многофазная фиксация транзакции

Каждым используемым в транзакции ресурсом управляет диспетчер ресурсов, действия которого координируются диспетчером транзакций. Перечисление ресурсов в качестве участников в разделе транзакций описывает, как ресурс (или несколько ресурсов) можно зачислить в транзакцию. В этом разделе также описывается порядок координации процесса фиксации транзакции между зачисленными ресурсами.

В конце транзакции приложение запрашивает ее фиксацию или откат. Диспетчер транзакций должен исключить ситуации, в которых один из диспетчеров ресурсов голосует за фиксацию, а другие - за откат транзакции.

Если транзакция включает несколько ресурсов, необходимо выполнить двухфазную фиксацию (2PC). Протокол двухфазной фиксации (фаза подготовки и фаза фиксации) гарантирует, что при завершении транзакции все изменения, произведенные над всеми ресурсами, либо полностью фиксируются, либо полностью откатываются. После завершения транзакции результат сообщается всем участникам. Подробное обсуждение протокола фиксации двухэтапной фиксации см. в книге "Обработка транзакций: основные понятия и методы (Морган Кауфманн серии в Управление данными системах) ISBN:1558601902" Джим Грей.

Кроме того, оптимизировать производительность транзакции можно путем участия в протоколе однофазной фиксации. Дополнительные сведения см. в статье "Оптимизация с помощью однофазной фиксации и уведомления о промотабельной однофазной фазе".

Если требуется только получение информации о результате транзакции без участия в голосовании, необходимо подписаться на событие TransactionCompleted.

Двухфазная фиксация (2PC)

В первой фазе транзакции диспетчер транзакций опрашивает каждый ресурс, чтобы определить, следует ли выполнить фиксацию или откат транзакции. Во второй фазе транзакции диспетчер транзакций уведомляет каждый ресурс о результате опроса, позволяя ресурсам выполнить необходимые операции очистки.

Для участия в такой транзакции диспетчеру ресурсов необходимо реализовать интерфейс 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;  
     }  
}  

После получения этого вызова диспетчеру устойчивых ресурсов необходимо записать в журнал данные для восстановления транзакции (доступ к которым осуществляется путем извлечения свойства RecoveryInformation) и всю необходимую информацию для завершения транзакции фиксацией. Запись этой информации в рамках метода Prepare не требуется, поскольку диспетчер ресурсов может сделать это в рабочем потоке.

После того, как диспетчер ресурсов завершил свои операции подготовки, ему необходимо проголосовать за фиксацию или откат путем вызова метода Prepared или ForceRollback. Обратите внимание, что класс PreparingEnlistment наследует метод Done от класса Enlistment. Если вызвать этот метод для обратного вызова PreparingEnlistment в фазе подготовки, он сообщает диспетчеру транзакций, что зачисленный ресурс доступен в режиме "только чтение" (т. е. диспетчеры ресурсов могут читать, но не могут изменять защищенные транзакцией данные), и диспетчер ресурсов не получает дальнейших уведомлений от диспетчера транзакций относительно результата транзакции в фазе 2.

Приложение получает сведения об успешной подготовке к фиксации транзакции после того, как все диспетчеры ресурсов проголосуют с помощью метода Prepared.

Фаза фиксации (фаза 2)

Если во второй фазе транзакции диспетчер транзакций получает сведения об успешной подготовке от всех диспетчеров ресурсов (все диспетчеры ресурсов вызвали метод Prepared в конце фазы 1), он вызывает метод 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();
}  

Диспетчер ресурсов должен выполнить любые действия, необходимые для завершения транзакции в соответствии с типом уведомления, и затем информировать диспетчер транзакций о завершении транзакции, вызвав метод Done для параметра Enlistment. Это может быть выполнено в рабочем потоке. Заметьте, что уведомления второго этапа могут быть встроены в тот же поток, который вызвал метод Prepared на первой фазе. Таким образом, после вызова метода Prepared не нужно выполнять никаких действий (например, снимать блокировки), которые должны были быть выполнены перед получением уведомлений второго этапа.

Реализация метода InDoubt

Наконец, необходимо реализовать метод InDoubt для диспетчера неустойчивых ресурсов. Этот метод вызывается при потере контакта диспетчера транзакций с одним или несколькими участниками, в результате чего их состояние становится неизвестным. В этом случае необходимо сделать соответствующую запись в журнале, чтобы можно было позднее выяснить, остался ли кто-либо из участников транзакции в несогласованном состоянии.

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

Оптимизация однофазной фиксации

Протокол однофазной фиксации наиболее эффективен при использовании во время выполнения, поскольку все изменения производятся без какой-либо явной координации. Дополнительные сведения об этом протоколе см. в разделе "Оптимизация с помощью однофазной фиксации" и уведомления о промотабельном однофазном уведомлении.

См. также