Commit di una transazione in monofase e multifase

Ogni risorsa utilizzata in una transazione viene gestita dalla gestione risorse (in seguito indicato con la sigla GR), le cui azioni vengono coordinate dalla gestione transazioni (in seguito indicato con la sigla GT). L'argomento Integrazione di risorse come partecipanti a una transazione illustra come integrare una risorsa o più risorse in una transazione. Questo argomento descrive invece come coordinare il commit di una transazione fra le risorse integrate.

Al termine della transazione l'applicazione ne richiede il commit o il rollback. La gestione transazioni deve eliminare qualsiasi possibilità che si verifichino situazioni di incoerenza, come ad esempio nel caso di una transazione per cui alcuni gestori di risorse votano a favore del commit mentre altri votano a favore del rollback.

Se la transazione interessa più di una risorsa, è necessario eseguire un commit a due fasi. Il protocollo di commit a due fasi, che prevede una fase di preparazione e una fase di commit, garantisce che al termine della transazione venga eseguito coerentemente il commit oppure il rollback di tutte le modifiche apportate alle risorse. Tutti i partecipanti vengono quindi informati in merito al risultato finale. Per informazioni dettagliate sul protocollo di commit a due fasi (2PC), consultare il libro sui concetti e sulle tecniche di elaborazione delle transazioni scritto da Jim Gray e appartenente alla collana Morgan Kaufmann sui sistemi di gestione dei dati (ISBN:1558601902).

Le prestazioni delle transazioni possono anche essere ottimizzate tramite il protocollo di commit monofase. Per altre informazioni, vedere Ottimizzazione mediante commit monofase e notifica monofase promuovibile.

Se si desidera soltanto ricevere informazioni sul risultato di una transazione senza partecipare alla votazione, è necessario essere registrati all'evento TransactionCompleted.

Protocollo 2PC

Nella prima fase della transazione, la gestione transazioni esegue una query su ogni risorsa allo scopo di determinare se eseguire il commit o il rollback di una transazione. Nella seconda fase della transazione, la gestione transazioni informa ogni risorsa in merito al risultato delle query eseguite, consentendo l'esecuzione delle operazioni di pulizia eventualmente necessarie.

Per poter partecipare a questo tipo di transazione, un gestore di risorse deve implementare l'interfaccia IEnlistmentNotification, che fornisce i metodi chiamati dal GT per l'invio delle notifiche durante un commit a due fasi. Di seguito è riportato un esempio di questa implementazione.

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

Fase di preparazione (fase 1)

Quando riceve dall'applicazione una richiesta di commit tramite una chiamata al metodo Commit, la gestione transazioni avvia la fase di preparazione di tutti i partecipanti integrati. A tale scopo, chiama il metodo Prepare su ogni risorsa integrata al fine di determinare per ognuna di esse il voto espresso in merito alla transazione.

Come illustrato nel semplice esempio seguente, il gestore di risorse che implementa l'interfaccia IEnlistmentNotification deve prima implementare il metodo 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;  
     }  
}  

Quando il gestore di risorse durevole riceve questa chiamata, deve registrare le informazioni per il ripristino della transazione (disponibili tramite il recupero della proprietà RecoveryInformation) nonché qualsiasi informazione necessaria al completamento della transazione in caso di commit. Non è necessario eseguire questa operazione all'interno del metodo Prepare, in quanto il GR può eseguirla mediante un thread di lavoro.

Dopo aver completato la fase di preparazione, il GR deve votare il commit o il rollback della transazione chiamando rispettivamente il metodo Prepared o il metodo ForceRollback. Si noti che la classe PreparingEnlistment eredita un metodo Done dalla classe Enlistment. Se si chiama questo metodo sul callback PreparingEnlistment durante la fase di preparazione, il GT riceve una notifica in cui si indica che l'integrazione è di sola lettura (ovvero, i gestori di risorse possono leggere ma non aggiornare i dati protetti dalle transazioni) e il GT non informa più il GR in merito al risultato della transazione nella fase 2.

L'applicazione riceve informazioni sull'esito positivo del commit della transazione dopo che tutti i gestori di risorse votano tramite il metodo Prepared.

Fase di commit (fase 2)

Nella seconda fase della transazione, se tutti i GR inviano al GT una conferma di esito positivo relativamente alla fase 1, ovvero se tutti i GR hanno richiamato il metodo Prepared al termine della fase di preparazione, il GT richiama il metodo Commit per ogni gestore di risorse. I gestori di risorse possono quindi rendere definitive le modifiche e completare il commit.

Se almeno uno dei gestori di risorse invia una conferma di esito negativo relativamente alla fase 1, la gestione transazioni richiama il metodo Rollback per ogni gestore di risorse e segnala all'applicazione la non riuscita del commit.

Ne consegue che il gestore di risorse deve implementare i metodi seguenti.

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

Il GR deve eseguire tutte le operazioni necessarie per completare la transazione in base al tipo di notifica e quindi informare il GT in merito chiamando il metodo Done sul parametro Enlistment. Queste operazioni possono essere eseguite su un thread di lavoro. Si osservi che nella fase 2 le notifiche possono presentarsi inline nello stesso thread che ha chiamato il metodo Prepared nella fase 1. Ne consegue che, dopo la chiamata al metodo Prepared, non occorre eseguire alcuna operazione (ad esempio il rilascio dei blocchi) che si prevede sia stata completata prima di ricevere le notifiche della fase 2.

Implementazione di InDoubt

Infine, è necessario implementare il metodo InDoubt per il gestore di risorse volatile. Questo metodo viene chiamato se la gestione transazioni perde il contatto con uno o più partecipanti, il cui stato risulta pertanto sconosciuto. In questo caso è necessario registrare questo problema in modo che in un secondo momento sia possibile verificare se uno o più partecipanti della transazione sono rimasti in uno stato incoerente.

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

Ottimizzazione mediante commit monofase

Il protocollo di commit monofase è più efficiente in fase di esecuzione, poiché tutti gli aggiornamenti vengono eseguiti senza alcuna coordinazione esplicita. Per altre informazioni su questo protocollo, vedere Ottimizzazione mediante commit monofase e notifica monofase promuovibile.

Vedi anche