DCSIMG
Services component transaction synchronization - IHateSpaghetti {code}

IHateSpaghetti {code}

VSX, DSL and Beyond by Eyal Lantzman

Syndication

Coding / Architecture

Extensibility /DSL

Projects

Articles

Services component transaction synchronization

We had interesting problem @work. We use serviced components as distributed transactions backend and we tried to use ReadCommited synchronization level to boost performance.

 

However we use some product that requires Serialized synchronization level – this is a problem because you can't run on single transaction (unless it uses serialized synchronization level).

So we thought of the following solution – run the components that requires serialized as RequiredNew (instead of Required – meaning, each instance of the services component will use a new transaction, instead of sharing existing one).

 

So far are you with me? Hope so.

 

OK so we have to different transaction but how can we control the commit / abort process if we need to run both of them using data calculated in each stage on different transactions? (see the flow below for example).

 

We need to write a little bit of code. But before that lets define the problems, the tools we have and than come up with a solution.

 

The problem: synchronizing two transactions – commit together / rollback together.

 

General flow

 

  1. Run code on T1
  2. Run code on T2
  3. Run code on T1
  4. Run code on T2
  5. if has error rollback T2 + T1
  6. else commit T2 + T1

The tools at our disposal: ContextUtil , [AutoComplete]

-          ContextUtil – setting done bit and consistent bit (more info)

-          [AutoComplete] – method level attribute that automatically sets the bits (on exception aborts trasaction) (more info)

 

The solution:

Manually use SetComplete / SetAbort in T2 context, in order to commit / rollback T2, without using AutoComplete attribute.

The Code in charge of T2 must provide commit / rollback public methods (they will use SetComplete / SetAbort inorder to run on T2 context).

 

Code snippet:

[Transaction(TransactionOption.Required)]
[Guid("....")]
public class Tran1 :ServicedComponent
{
  [AutoComplete]
  public void DoWork()
  {
    string data = GetDataOnTran1();
    using (Tran2 tran2 = new Tran2)
    {
      try{
      //do work with data from transaction 1 on transaction 2
      //becarefull about using resources that haven't been commited yey
      //such as tables with FK
      string processedData = tran2.DoWork(data)
      
      //process data returned from transaction 2 on transaction 1
      string evenMoreProcessedData = DoMoreWork(processedData);

      //processes data again on trasaction 2
      data = tran2.DoMoreWork(evenMoreProcessedData );

      //Manualy commit data on trasaction 2 (trasaction 1 not commited yet)
      tran2.CommitData();

      }
      catch
      {
        //notice the manul rollback of transaction 2
        tran2.RollbackData();
        
        throw;
      }
    }
  }

  private string DoMoreWork(string someWork)
  {
    ...
  }
}


[Transaction(TransactionOption.RequiredNew)]  //new trasaction
[Guid("....")]
public class Tran2 :ServicedComponent
{
  //no autocomplete here
  public string DoWork()
  {
    ...      
  }

  private string DoMoreWork(string someWork)
  {
    ...
  }

  public void CommitData()
  {
    //notice that when using context util methods, they run in CONTEXT of the transaction, meaning on transaction 2
    ContextUtil.SetComplete();
  }

  public void RollbackData()
  {
    //notice that when using context util methods, they run in CONTEXT of the transaction, meaning on transaction 2
    ContextUtil.SetAbort();
  }
}

 

Final remarks:

The solution is not bullet proof – in case of error/timeout when T2 is already committed, there's no way to perform rollback and we get inconsistent data (T1 will perform rollback). However  this is better that nothing

Published Thursday, May 31, 2007 10:28 AM by Eyal
תגים:,

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: