Welcome to MSDN Blogs Sign in | Join | Help

When not to use transactions and where you can't use transactions

Transactions are great when everyone plays in. You group a set of activities together under a transaction and you start executing them. If anything bad happens along the way or something doesn't go as planned, invoke rollback and all is taking care of automatically by the infrastructure: everything is undone and the app returns to the state before the transaction started; recovery and consistency is a piece of cake with nothing to worry about.

 

But...

 

I can't really use transactions for long running sequences of activities. I mean, technically is possible, but the app will not scale anymore. Imagine starting the transaction today, updating databases and doing transactional work for a few days; add to that a few human interventions (when user input is required) and make it a few more days longer and only after that commit the transaction. What just happened? For those number of days, the transactional work kept all the resources that it touched in a locked state waiting for the commit (or rollback) decision. All these resources could not be used by anybody else (with various degrees of freedom depending on your isolation level) for that many days. Transactions are really meant for short lived work.

 

Another aspect, partial rollback - the ability to rollback some work in the case of an event, but let the rest of the work that executed so far to continue towards a possible commit. This is not possible today with how transactions are implemented: have at least one little piece of work or resource declare itself in error and the whole transaction will rollback, no exceptions allowed.

 

And then, maybe the most major aspect, not everything participates in a transaction. It can be a resource, it can be an action that can't rollback. Implementing resources that are transactional is not trivial, meeting all the ACID properties is hard. Or in some cases, it just doesn't make sense to make something transactional, like sending an email. What to rollback there?. Chase the internet to catch the email before it reaches ones inbox, and if it got to the inbox, tell the reader to forget it?

 

In all these cases, something else needs to be used in combination with short lived transactions. That means that you need to provide code that un-does/cancels/compensates the work that was already done. You need to write most of the "undo" code since the rules need to be more relaxed than with transactions. The compensation logic varies depending on the application logic at that given time. In the transactions world, the ACID requirements govern everything and there are always only two states: an initial state before transaction and a state after the transaction commits; rollback will always move the app back to the initial state. With compensation, the number of consistency states depends on the app and are defined by the app.

 

Support for compensation exists today in Biztalk Server and Windows Workflow.

MSDTC and COM+ Configuration Tool in Vista

To access the MSDTC and COM+ configuration tool in Windows Vista, also known as Component Services MMC, you have the following options:

1.       Run “dcomcnfg”

2.       Or run: %SystemRoot%\System32\comexp.msc

3.       Or if you prefer, you can add a shortcut to %SystemRoot%\System32\comexp.msc in Control Panel

 

If you have UAC on and you want to do 3, first make a shortcut for %SystemRoot%\System32\comexp.msc on your Desktop and then move it to Control Panel.

Posted by florinlazar | 0 Comments
Filed under:

Exception Handling without catch(Exception)

The design guidelines for exception handling are quite clear on avoiding “catch all” and/or avoiding catching exceptions you can’t handle. But there are cases when you really need to know if the try block completed successfully or not, and possibly take some action.

 

One way one could achieve this without breaking the guideline is to set a flag at the end of the try block and then query it inside finally:

 

bool workCompleted = false;

try

{

 

    DoSomething();

    DoSomethingMore();

    workCompleted = true;

}

finally

{

    if (!workCompleted)

    {

        Console.WriteLine("record that the work failed");

    }

}

 

Posted by florinlazar | 2 Comments
Filed under:

Working with CMD and Long Path Names

I like using the command prompt for a lot of things and working with long (or very long) path names is something that is common these days.

Fortunately, you can customize cmd by using "prompt". Do a “prompt /?” in a command window  to see all the options.

I like it simple and I use: “PROMPT=$p$_$+$g”. This gives:

 

C:\Windows\System32\Msdtc\Trace

> 

 

To persist it, set it as an environment variable.

Posted by florinlazar | 0 Comments
Filed under:

How to Use System.Data with System.Transactions and Maintain Atomicity and Data Consistency

The Bug

I call it a bug. Initially I was persuaded to believe it was a feature; later I was "convinced" that it is now a matter of app compat and it can't be changed anymore.

 

Let’s look at the following code:

 

SqlConnection connection1 = new SqlConnection(connectionString);

 

using(TransactionScope txScope = new TransactionScope())

{

    connection1.Open();

 

    SqlCommand command1 = new SqlCommand(commandString1, connection1);

    command1.ExecuteNonQuery();

 

    SqlCommand command2 = new SqlCommand(commandString2, connection1);

    command2.ExecuteNonQuery();

 

    txScope.Complete();

}

 

 

What are your expectations from this code? I’m sure you are saying that both commands will execute with success or none will execute, for instance if command1 succeeds and command2 fails and throws, the transaction rollbacks and command1 is undone. Right?

 

Let’s add more data to the equation: transactions can abort at any time due to various reasons, most common one being timeouts. Let’s imagine that the transaction timeout is 30 seconds. Then, let’s imagine that command1 takes 29.(9) seconds to execute. This means that the transaction aborts immediately after that. Let’s imagine that it aborts before command2 starts executing. Now, what are the expectations here? Well, transaction aborts and then command1 is undone and everything goes back to the state before the transaction was started.

Unfortunately, by using the defaults for System.Data this is not what happens. What really happens is:

-          command1 is undone

-          command2 is executed outside the transaction

There is no typo there, command2 executes even if the transaction aborted. In other words, if one expected atomicity for the code inside TransactionScope, the code just corrupted the data due to the partial rollback.

 

The “Feature”

 Apparently the following code is common:

 

SqlConnection connection1 = new SqlConnection(connectionString);

 

using(TransactionScope txScope = new TransactionScope())

{

    connection1.Open();

 

    SqlCommand command1 = new SqlCommand(commandString1, connection1);

    command1.ExecuteNonQuery();

 

    SqlCommand command2 = new SqlCommand(commandString2, connection1);

    command2.ExecuteNonQuery();

 

    txScope.Complete();

}

 

SqlCommand command3 = new SqlCommand(commandString3, connection1);

command3.ExecuteNonQuery();

 

In other words, the “feature” allows the use of the connection in auto-commit mode after it was previously used in a transaction. The connection automatically unbinds from the transaction when the transaction completes (either abort or commit) and becomes an auto-commit non-transacted connection.

 

The App Compat Issue

Now that we know that the automatic unbind can lead to data corruption, changing it to not unbind automatically will make the code for command3 to fail to execute, since the connection1 is trying to run a command in a transaction that completed. Thus, if the code is indeed common, changing the behavior of the connection will indeed break existing apps.

 

The Solution

The solution implemented by System.Data in “Orcas” is to pass a new keyword to the connection string called “Transaction Binding” and give it the value of “Explicit Binding” to disable the auto-unbind. This will cause command2 to try to execute under the same transaction as command1, even after the abort occurred. See ConnectionString and Integration with Transactions for details.

The following thread might also be useful: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3055387&SiteID=1

 

Hopefully, the default behavior will change to explicit unbind in the future releases (non-SP) of .Net Framework.

Posted by florinlazar | 2 Comments

Limitation of TransactionScope (and using)

If you read the documentation for TransactionScope, you will find: "If no exception occurs within the transaction scope […], then the transaction in which the scope participates is allowed to proceed. If an exception does occur within the transaction scope, the transaction in which it participates will be rolled back." Later the doc says: “When your application completes all work it wants to perform in a transaction, you should call the Complete method only once to inform that transaction manager that it is acceptable to commit the transaction. Failing to call this method aborts the transaction.”

 

OK, so how about this code:

 

using(TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew)) {

    // do transactional work

 

    ts.Complete();

    throw new Exception();

}

 

What happens to the transaction after executing this code? Take a guess. If we follow the doc, the transaction roll backs, right? Well, the transaction actually commits – try it out!

 

Let’s see what happens by “unpacking” the using statement:

     

TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew);

try {

    // do transactional work

 

    ts.Complete();

    throw new Exception();

}

finally {

    ts.Dispose();

}

 

In the Dispose of TransactionScope something similar to the following pseudo-code happens:

 

       if Complete was called {

              Transaction.Commit();

}

else {

       Transaction.Rollback();

}
 

The limitation really comes from the fact that at Dispose time in finally, the using construct doesn’t offer any information about completion of its body with exception or not. That is why the Transaction.Complete was invented and that is why the documentation for Complete says: “It is very good practice to put the call as the last statement in the using block.” Complete was invented to detect exceptions, i.e. if an exception occurs, Complete will not get called – but this work only if one follows the guideline to put Complete as the last statement. Doing any work after Complete was called is asking for trouble and possible data corruption. 

 

If you want to be on the safe side, use my transacted construct which doesn’t have these issues. It follows a simple rule: if an exception is thrown, the transaction aborts, otherwise it commits.

Posted by florinlazar | 0 Comments
Filed under: ,

Avoiding Transaction Promotion with Multiple Connections - Improvements in System.Data and SQL Server 2008

Great news! The new updates added to System.Data and SQL Server 2008 finally allow multiple Open/Close connections to the same SQL Server without promoting the transaction to MSDTC.

This was by far the most requested feature for the System.Transactions/System.Data/SQL Server combination.

There is no more the need to write workarounds like ConnectionScope to avoid promotions.

Now I can write as many Open/Close, Open/Close as I need to, without worrying about promotion:

transacted(()=>
{
   using (SqlConnection connection1 = new SqlConnection(connectionString))
   {
      Connection1.Open();

      SqlCommand command1 = new SqlCommand(commandString1, connection1);
      command1.ExecuteNonQuery();

      SqlCommand command2 = new SqlCommand(commandString2, connection1);
      command2.ExecuteNonQuery();

      connection1.Close();
   }

   ...

   using (SqlConnection connection2 = new SqlConnection(connectionString))
   {
      Connection2.Open();

      SqlCommand command1 = new SqlCommand(commandString1, connection2);
      command1.ExecuteNonQuery();

      SqlCommand command2 = new SqlCommand(commandString2, connection2);
      command2.ExecuteNonQuery();

      connection2.Close();
   }
});

More details on the updates at the ADO.Net team blog.

A Simpler TransactionScope

In .Net 3.5 I can write a transactional code block as follows:

transacted(()=>
{
   using (SqlConnection connection = new SqlConnection(connectionString))
   {
      connection.Open();

      SqlCommand command1 = new SqlCommand(commandString1, connection);
      command1.ExecuteNonQuery();

      SqlCommand command2 = new SqlCommand(commandString2, connection);
      command2.ExecuteNonQuery();
   }
});

No need for using and no need to remember to call Complete at the end of the block.

To enable this I need to write just a few lines of code:

delegate void TransactedCodeDelegate();
void transacted(TransactedCodeDelegate txCode)
{
   using (TransactionScope ts = new TransactionScope())
   {
      txCode();
      ts.Complete();
   }
}
Posted by florinlazar | 3 Comments
Filed under:

Flowing or propagating transactions in .Net

System.Transactions transactions are bounded to the appdomain. Which means that if you make in-appdomain calls from inside a TransactionScope, those calls will share the transaction. This also means that if you need to make a call outside the current appdomain, or outside the process, you will need to "flow" the transaction.

To flow a System.Transactions transaction, there are a few options:

a. if you are using serialization
 

//sender code
using(TransactionScope ts = new TransactionScope())
{
  RemoteCallUsingSerialization(Transaction.Current, other parameter); // this is the call to other appdomain/process
  // notice I added a new parameter of type System.Transactions.Transaction  

  ts.Complete();
}

//destination code
void RemoteCallUsingSerialization(System.Transactions.Transaction tx, other parameter)
{
  using(TransactionScope ts2 = new TransactionsScope(tx))
  {
   // access transactional resourses like a database; this code will execute as part of the same transaction
   ts2.Complete();
  }
}

b. if you are not using serialization
 

//sender code
using(TransactionScope ts = new TransactionScope())
{  
  RemoteCall(TransactionInterop.GetTransmitterPropagationToken(Transaction.Current), other parameter); // this is the remote call
  // notice I added a new parameter of type byte[]  

  ts.Complete();
}

//destination code
void RemoteCall(byte[] tx, other parameter)
{
  using(TransactionScope ts2 = new TransactionsScope(TransactionInterop.GetTransactionFromTransmitterPropagationToken(tx)))
  {
   // access transactional resourses like a database;
   // this code will execute as part of the same transaction
   ts2.Complete();
  }
}

c. if .Net 3.0 or higher is an option, you can use WCF and its support for transactions using attributes: http://msdn2.microsoft.com/en-us/library/aa347993.aspx

Remark: When the transaction is flown outside the current appdomain, it will get upgraded to a distributed transaction (or MSDTC transaction).

If you are not using .Net, see http://blogs.msdn.com/florinlazar/archive/2004/10/02/236965.aspx for propagating transactions in C++.

TIP is now deprecated

TIP is getting deprecated - see the note at http://msdn2.microsoft.com/en-us/library/ms679484(VS.85).aspx
If the native OleTx protocol used by MSDTC and System.Transactions is not friendly enough for your firewall, you should consider the WS-AtomicTransactions protocol provided through Windows Communication Foundation in .Net Framework 3.0 and higher: http://msdn2.microsoft.com/en-us/library/ms730266.aspx
Posted by florinlazar | 0 Comments
Filed under:

MSDN Magazine Article on Transactional File System

Jason Olson describes the Transactional NTFS and how you can take advantage of it in your applications: http://msdn.microsoft.com/msdnmag/issues/07/07/NTFS/default.aspx

Posted by florinlazar | 0 Comments
Filed under:

WCF in Romania

These days I'm in Romania with a tour at 3 universities (in Bucharest, Cluj and Iasi) talking about Windows Communication Foundation, distributed systems and web services. Adi Oltean is also talking about storage technologies in Windows.

So far the seminars were received really well and we made a lot of friends. I'm really pleased to be able to contribute to the adoption of WCF and to demonstrate the amazing value and power of WCF with its productivity, interoperability and extensibility features.

 More details at http://reg.studentclub.ro/RegisterForEvent.aspx?idEveniment=55 (in romanian).

 I would like to thank Todi Pruteanu for organizing these events.

Posted by florinlazar | 3 Comments
Filed under:

"Orcas" March 07 CTP: Support for OASIS WS-AT 1.1 and WS-Coord 1.1

In the "Orcas" March 2007 CTP for Visual Studio and .Net, Windows Communication Foundation brings support for OASIS specifications WS-AtomicTransaction 1.1 and WS-Coordination 1.1. WCF will have side-by-side support with the 1.0 versions of these specs.

To use the new version of WS-AT, you can specify the new WS2007HttpBinding, or, if you already use the NetTcpBinding, you can set the TransactionProtocol to WSAtomicTransactions11.

Posted by florinlazar | 3 Comments
Filed under:

Allowing Transactions into Your Component: "Do you really want that?"

Supporting transactions is part of your component contract. If you shipped a component yesterday that exposed MyComp.DoWork to the public, and today you want to add transactional support to your component, you shouldn't use the same method MyComp.DoWork and make it transactions aware. Not unless you want to break your customers (see http://blogs.msdn.com/florinlazar/archive/2005/04/19/409834.aspx for details on how you can break existing apps). Instead you should publish a new method, MyComp.TransactedDoWork, that you document it as being transactions aware, i.e. it will participate in the ambient transaction if one is available.

The purpose of this post is not about TransactedDoWork, but rather about a new method, MyComp.DoMoreWork, that you want to add to your v2. Let's assume that in this method, you write an entry into a file, call a webservice and update two databases. Since you learned about System.Transactions, you are using TransactionScope to make the updates to the two databases. And since most of the samples use the default constructor for TransactionScope, you are probably doing it the same way:

using (TransactionScope ts = new TransactionScope())
{
    // open connection to db1
    // update db1
    // close connection to db1
    // open connection to db2
    // update db2
    // close connection to db2
    
    ts.Complete();
}

All appears to be good and so you ship MyComp v2. After a while, you start getting reports from your customers saying that sometimes, MyComp.DoMoreWork doesn't work properly: they see the update to the file, they see the webservice being called but the updates to databases are missing. What is going on?

Well, the default constructor for TransactionScope uses TransactionScopeOption.Required, which enlists in the ambient transaction if one exists. Thus, if your customers are using transactions and they call MyComp.DoMoreWork, and if the transaction aborts, part of the work done by DoMoreWork is going to be rollbacked!! But that wasn't what you intended.

The best practice is to always "protect" your outermost TransactionScopes in our component from enlisting in the ambient transaction by using a non-default constructor,  TransactionScope(TransactionScopeOption.RequiresNew) or TransactionScope(TransactionScopeOption.Suppress), depending on your particular scenario. Of course, if you want the transaction to flow inside the component you can use the default constructor, but that should be a conscious decision.

Given that the TransactionScope is usually hidden into the call stack of your component method, for instance:

void MyComp.DoMoreWork
{
    internalDoTheRealMoreWork(); // the first TransactionScope shows up inside internalDoTheRealMoreWork
}

The best way to deal with unwanted transactions is to start your public interface methods with a non-default TransactionScope:

void MyComp.DoMoreWork
{
    using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Suppress))
    {
        internalDoTheRealMoreWork();		
        ts.Complete();
    }
}

The reason why TransactionScope default is Required is to allow easy creation of imbricated or nested TransactionScopes (not to be confused with "nested transactions"):

void internalDoTheRealMoreWork()
{
    using (TransactionScope ts = new TransactionScope())
    {
        // work
        myOtherInternalWork();
        ts.Complete();
    }
}
void myOtherInternalWork();
{
    using (TransactionScope ts = new TransactionScope())
    {
        // do other work
        ts.Complete();
    }	
}

With the default constructor you write less code and the transactions "flows" nicely from method to method in the call stack. You are supposed to use the non-default constructors for TransactionScope, when you want to make a decision not to allow the ambient transaction inside your component or method. One of those cases is in the public interface methods.

Next time you write a public method, always ask yourself - "do I want to let a transaction in or not?" - if the answer is no, then start your public method with a non-default TransactionScope. And remember to document your method's behavior with regards to transactions.

Supporting Promotable Transactions and Phase 0

If you are providing support for promotable transactions for your resource manager using PSPE then you need to remember to "support" Phase 0.

In general, a resource manager doesn't need to do anything special about Phase 0; this phase will be handled solely by the transaction manager. But, because in the case of promotable transactions using the PSPE mechanism, the resource manager acts as a special "transaction manager", your resource manager needs to be aware of Phase 0. Why? Because in this case, the resource manager "owns" the distributed transaction and System.Transaction will call Commit on the PSPE interface asking the resource manager to call Commit on the distributed transaction. The resource manager needs to be aware that even after it will call Commit on the distributed transaction it owns, there is a possibility that requests to enlist and do work as part of the same transaction are still possible, based on the Phase 0 rules. And these requests are supposed to be accepted. Don't worry, there will be no data corruption. MSDTC guarantees that Phase 0 happens before Phase 1 starts delivering its "prepare" messages.

What exactly you need to do to ensure Phase 0 support? Let's create the list:

  1. when you receive the "commit" request through the PSPE interface, call Commit on the distributed transaction but do not start the commit process on your data yet; wait for the Prepare message on the distributed transaction from MSDTC before doing any work (this assumes you are enlisted with that transaction - you should be!); in other words, when you receive the IPromotableSinglePhaseNotification::SinglePhaseCommit simply "pass" along the Commit to the distributed trasaction you own and don't do anything else
  2. don't get confused by the name of IPromotableSinglePhaseNotification::SinglePhaseCommit; it is not the same as the classic single phase commit from MSDTC; there might be phase 0 enlistments out there that might enlist other durable resource managers in the transaction, and thus a 2-Phase-Commit might be necessary
  3. even after calling Commit on the distributed transaction, you should continue to accept new connections and work on the same transaction; don't worry, phase 1 didn't start yet, these are just phase 0 enlistments "flushing" their data to durable stores
  4. once you received the "prepare" message from MSDTC on the distributed transaction, only then you should start your normal 2PC operation
  5. if any new request is received on the same transaction after "prepare" was received from MSDTC, you should deny it as you do in any 2PC scenario after phase 1 started

Sounds like a lot of steps, but in reality, this can be said in a short summary: "when you receive IPromotableSinglePhaseNotification::SinglePhaseCommit, pass along the Commit to the distributed transaction you own and behave like it didn't happen :), i.e. accept new connection requests in the same transaction until MSDTC delivers you the "prepare" message.

Of course, if Promote was never called, then there isn't any distributed transaction involved and thus you can proceed with commiting the work when IPromotableSinglePhaseNotification::SinglePhaseCommit is called.

Related posts:

Thanks for supporting promotable transactions and Phase 0.

More Posts Next page »
 
Page view tracker