none
Rollback of System.Transactions is not in FILO way?

    Question

  • Hi all,
    As taught in classic transaction theory, transaction rollback is conducted in a First In Last Out way, which the most recent operations are undo first.
    But when I run this code following to take a look of how the order of rollback in LTM of System.Transactions, I found the order is in a FIFO way, am I wrong that rollback is not always necessary to be done in a FILO way?

    Set: 0
    Set: 1
    Set: 2
    Rollback: 0
    Rollback: 1
    Rollback: 2
    Rollback: 0
    //Why rollback is not from 2, 1, 0?

    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Transactions;

    namespace TxDemo
    {
    class RollbackOrder
    {
    static void Main(string[] args)
    {
    try
    {
    using (TransactionScope ts = new TransactionScope())
    {
    for (int i = 0; i < 5; i++)
    {
    SimpleEnlist enlist = new SimpleEnlist();
    enlist.Index = i;
    }
    ts.Complete();
    }
    }
    catch (Exception ex)
    {

    }
    }
    }

    public class SimpleEnlist : IEnlistmentNotification
    {
    protected int index = 0;

    public SimpleEnlist()
    {
    Transaction.Current.EnlistVolatile(this, EnlistmentOptions.None);
    }

    public int Index
    {
    get
    {
    return this.index;
    }
    set
    {
    if (value == 3)
    {
    throw new Exception();
    }
    Console.WriteLine("Set: {0}", value);
    this.index = value;
    }
    }

    #region IEnlistmentNotification
    void IEnlistmentNotification.InDoubt(Enlistment enlistment)
    {
    // does nothing
    }

    void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
    {
    preparingEnlistment.Prepared();
    }

    void IEnlistmentNotification.Commit(Enlistment enlistment)
    {
    Console.WriteLine("Commit: {0}", index);
    enlistment.Done();
    }

    void IEnlistmentNotification.Rollback(Enlistment enlistment)
    {
    Console.WriteLine("Rollback: {0}", index);
    enlistment.Done();
    }
    #endregion
    }
    }
    Tuesday, April 03, 2007 10:12 AM

All replies

  • I do not believe this is a requirement as long as transaction rolls back everything properly. Why do you care about order in your case? It will be one atomic transaction and for you application it will be one logical step anyway.

    Tuesday, April 03, 2007 10:17 AM
  • Hi VMazur,
    Suppose there are two tables, A and B; B has a foreign key to A's primary key. For the transaction, we first insert a new record to table A, and then insert another record to table B with the foreign key to the primary key of the new record of table A. At the moment a rollback occurred, don't we need to first remove the new record in table B before remove the new record in table A? So I think rollback is run in a FILO manner.

    As I found in Jim Gray's lecture ppt

    declare cursor for transaction_log
    select rmid, lsn                                     /* a cursor on the transaction's log */
    from log                                             /* it returns the resource manager name */
    where trid = :trid                                 /* and record id (log sequence number) */
    descending lsn;                                 /* and returns records in LIFO order */
    void transaction_undo(TRID trid) /* Undo the specified transaction. */
       {  int sqlcode;                             /* event variables set by sql */
    open cursor transaction_log;          /* open an sql cursor on the trans  log */
    while  (TRUE)                               /* scan trans log backwards & undo each*/
    {                                                  /* fetch the next most recent log rec */
    fetch transaction_log into :rmid, :lsn; /*   */
    if (sqlcode != 0) break;                  /* if no more,  trans is undone, end loop */
           rmid.undo(lsn);                         /* tell RM to undo that record */
           }                                             /* tell RM to undo that record */
       close cursor transaction_log;         /* Undo scan is complete, close cursor  */
       };                                                 /* return to caller */
    Wednesday, April 04, 2007 3:04 AM
  • Since you do not remove any rows explicitly, you do not need to follow specific order in this case. Database engine will do it properly and clean.  As long as result after rollback is the same as before you started transaction, it is fine. You have to follow FILO if you delete records using your own way, because you could get referential integrity error, but database engine has its own algorithm to handle data inside of the transaction, which does not mean it actually deletes the data.

    Wednesday, April 04, 2007 10:23 AM