<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Florin Lazar - Consistency Checkpoint : promotable transactions</title><link>http://blogs.msdn.com/florinlazar/archive/tags/promotable+transactions/default.aspx</link><description>Tags: promotable transactions</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Supporting Promotable Transactions and Phase 0</title><link>http://blogs.msdn.com/florinlazar/archive/2007/02/10/supporting-promotable-transactions-and-phase-0.aspx</link><pubDate>Sat, 10 Feb 2007 16:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1643248</guid><dc:creator>florinlazar</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/florinlazar/comments/1643248.aspx</comments><wfw:commentRss>http://blogs.msdn.com/florinlazar/commentrss.aspx?PostID=1643248</wfw:commentRss><wfw:comment>http://blogs.msdn.com/florinlazar/rsscomments.aspx?PostID=1643248</wfw:comment><description>If you are providing support for promotable transactions for your resource manager using PSPE&amp;nbsp;then you need to remember to "support" Phase 0. 
&lt;P mce_keep="true"&gt;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. &lt;/P&gt;
&lt;P mce_keep="true"&gt;What exactly you need to do to ensure Phase 0 support? Let's create the list:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;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&lt;/LI&gt;
&lt;LI&gt;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&lt;/LI&gt;
&lt;LI&gt;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&lt;/LI&gt;
&lt;LI&gt;once you received the "prepare" message from MSDTC on the distributed transaction, only then you should start your normal 2PC operation&lt;/LI&gt;
&lt;LI&gt;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&lt;/LI&gt;&lt;/OL&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Related posts: &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;Phase 0: &lt;A href="http://blogs.msdn.com/florinlazar/archive/2006/01/29/msdtc-the-magic-of-phase-zero-phase0-or-when-using-2pc-transactions-is-not-enough.aspx" mce_href="http://blogs.msdn.com/florinlazar/archive/2006/01/29/msdtc-the-magic-of-phase-zero-phase0-or-when-using-2pc-transactions-is-not-enough.aspx"&gt;http://blogs.msdn.com/florinlazar/archive/2006/01/29/msdtc-the-magic-of-phase-zero-phase0-or-when-using-2pc-transactions-is-not-enough.aspx&lt;/A&gt; and &lt;A href="http://blogs.msdn.com/florinlazar/archive/2006/04/09/572035.aspx" mce_href="http://blogs.msdn.com/florinlazar/archive/2006/04/09/572035.aspx"&gt;http://blogs.msdn.com/florinlazar/archive/2006/04/09/572035.aspx&lt;/A&gt; &lt;/DIV&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV mce_keep="true"&gt;PSPE:&amp;nbsp;&lt;A href="http://blogs.msdn.com/florinlazar/archive/2005/05/17/418595.aspx" mce_href="http://blogs.msdn.com/florinlazar/archive/2005/05/17/418595.aspx"&gt;http://blogs.msdn.com/florinlazar/archive/2005/05/17/418595.aspx&lt;/A&gt;&lt;/DIV&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;Thanks for supporting promotable transactions and Phase 0.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1643248" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/florinlazar/archive/tags/promotable+transactions/default.aspx">promotable transactions</category><category domain="http://blogs.msdn.com/florinlazar/archive/tags/phase0/default.aspx">phase0</category></item><item><title>When transaction promotion goes "bad"</title><link>http://blogs.msdn.com/florinlazar/archive/2007/02/08/when-transaction-promotion-goes-bad.aspx</link><pubDate>Thu, 08 Feb 2007 14:41:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1626594</guid><dc:creator>florinlazar</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/florinlazar/comments/1626594.aspx</comments><wfw:commentRss>http://blogs.msdn.com/florinlazar/commentrss.aspx?PostID=1626594</wfw:commentRss><wfw:comment>http://blogs.msdn.com/florinlazar/rsscomments.aspx?PostID=1626594</wfw:comment><description>&lt;P&gt;We already know the benefits of promotable transactions. They are all about performance, being very fast and lightweight.&lt;/P&gt;
&lt;P&gt;But then you get into situations where you know for sure that you are going to talk to a second resource manager, so you know you will get to a distributed transaction anyway. Or, the resource managers out there that support promotable transactions might have limitations when you put them into edge cases. But you really want to use that edge case and it doesn't work.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Well, one solution is to "turn off" promotion and force a distributed transaction from the beginning such that all the resource managers involved will follow the classic route of enlisting in the transaction. We don't actually provide a flag today in System.Transactions to "turn off" promotable transactions, but you can achieve it by forcing the transaction to be a distributed transaction before you open any resource manager connection.&lt;/P&gt;
&lt;P mce_keep="true"&gt;The way you do it in code is by calling:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TransactionInterop.GetTransmitterPropagationToken(tx); &lt;/P&gt;
&lt;P&gt;Where tx is the transaction you want to promote.&lt;/P&gt;
&lt;P&gt;If you are using TrasactionScope similar to:&lt;/P&gt;&lt;PRE class=csharpcode&gt;using(TransactionScope ts = new TransactionScope())
{
    connection.Open();
    // do the work on the first connection
    connection2.Open();
    // do the rest of the work
    ts.Complete();
}&lt;/PRE&gt;Then the code with the workaround should be: &lt;PRE class=csharpcode&gt;using(TransactionScope ts = new TransactionScope();)
{
    TransactionInterop.GetTransmitterPropagationToken(Transaction.Current);
    connection.Open();
    // do the work on the first connection
    connection2.Open();
    // do the rest of the work
    ts.Complete();
}&lt;/PRE&gt;
&lt;P&gt;With time, resource managers will get better. But today, you might find this &amp;lt;trick&amp;gt; useful.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1626594" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/florinlazar/archive/tags/promotable+transactions/default.aspx">promotable transactions</category></item><item><title>Writing a resource manager that supports promotable transactions (or Promotable Single Phase Enlistment aka PSPE) in System.Transactions</title><link>http://blogs.msdn.com/florinlazar/archive/2005/05/17/writing-a-resource-manager-that-supports-promotable-transactions-or-promotable-single-phase-enlistment-aka-pspe-in-system-transactions.aspx</link><pubDate>Tue, 17 May 2005 17:14:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:418595</guid><dc:creator>florinlazar</dc:creator><slash:comments>11</slash:comments><comments>http://blogs.msdn.com/florinlazar/comments/418595.aspx</comments><wfw:commentRss>http://blogs.msdn.com/florinlazar/commentrss.aspx?PostID=418595</wfw:commentRss><wfw:comment>http://blogs.msdn.com/florinlazar/rsscomments.aspx?PostID=418595</wfw:comment><description>&lt;P&gt;A key feature that targets performance in System.Transactions is the Promotable Single Phase Enlistment. It allows a durable resource manager (RM) to host and "own" a transaction that can later be promoted to a distributed transaction (or MSDTC transaction) if necessary. This specific resource manager usually has its own internal non distributed transactions and it needs to support changing those transactions to distributed transactions at runtime.&lt;/P&gt;
&lt;P&gt;The overview of steps involved in PSPE is:&lt;BR&gt;1. The RM proxy enlists for PSPE using Transaction.EnlistPromotableSinglePhase. If the enlistment succeeds, the RM usually creates its internal transaction and associates it with the System.Transactions transaction. The notification are later sent to the RM proxy using the IPromotableSinglePhaseNotification interface&lt;BR&gt;2. If the System.Transactions transaction never requires a promotion (see previous post &lt;A href="http://blogs.msdn.com/florinlazar/archive/2005/05/12/416805.aspx" mce_href="http://blogs.msdn.com/florinlazar/archive/2005/05/12/416805.aspx"&gt;http://blogs.msdn.com/florinlazar/archive/2005/05/12/416805.aspx&lt;/A&gt;) then, when the transaction is committed, the RM proxy will receive a SinglePhaseCommit, at which point it can commit the internal transaction that was initially created&lt;BR&gt;3. If the System.Transactions transaction needs to be promoted (to support multiple RMs for instance), then System.Transactions will ask the RM to promote the transaction to a distributed transaction. The RM will have to promote the internal transaction to an MSDTC transaction, and associate it with the work already done. Later, when System.Transactions will commit its transaction, it will send a SinglePhaseCommit notification to the RM proxy and the RM will have to commit the distributed transaction that it created during promotion.&lt;/P&gt;
&lt;P&gt;Please note that at step 1, I had an "if the PSPE enlistment succeeds" condition. This means that PSPE is not always allowed by System.Transactions. The cases when this can happen are: 1) the transaction is already a distributed transaction or 2) another RM has already done a PSPE enlistment. If the PSPE enlistment fails, the RM will have to follow the regular rules for enlistment (marshal the transaction to the RM and enlist using DurableEnlist).&lt;/P&gt;
&lt;P&gt;Let's look at code now. I will have a client and a server communicating using remoting:&lt;/P&gt;
&lt;P&gt;&amp;lt;client.cs&amp;gt;&lt;BR&gt;&lt;PRE class=csharpcode&gt;using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Transactions;

namespace PSPEClient
{
    class Client
    {
        static void Main(string[] args)
        {
            using (TransactionScope ts = new TransactionScope())
            {
                /*section1begin
                DurableRM durableRM = new DurableRM();
                durableRM.OpenConnection();
                durableRM.DoWork();
                /*section1end*/
                
                DatabaseProxy dbProxy = new DatabaseProxy();
                dbProxy.OpenConnection();
                dbProxy.DoWork();
                
                /*section2begin
                DurableRM durableRM = new DurableRM();
                durableRM.OpenConnection();
                durableRM.DoWork();
                /*section2end*/
                
                ts.Complete();
            }
            System.Console.ReadLine();
        }
        
        class DatabaseProxy
        {
            private PSPEServer.PSPEDatabaseServer db;
            private InternalRM internalRM;
            
            public void OpenConnection()
            {
                System.Console.WriteLine("DatabaseProxy.OpenConnection");
                // connecting to the remote database
                TcpChannel tcpChannel = new TcpChannel();
                ChannelServices.RegisterChannel(tcpChannel);
                this.db = (PSPEServer.PSPEDatabaseServer)Activator.GetObject(
                    typeof(PSPEServer.PSPEDatabaseServer), "tcp://localhost:8085/MyDatabase");
                if (null == db)
                {
                    System.Console.WriteLine("Cannot connect to the server");
                }
                else
                {
                    System.Console.WriteLine("Internal tx id:" + db.Connect());
                }
                
                // enlisting in the transaction
                if (null != this.internalRM)
                {
                    throw new System.Exception("we don't support multiple connections, this is just a sample");
                }
                this.internalRM = new InternalRM(db);
                this.internalRM.Enlist();
            }
            
            public void DoWork()
            {
                System.Console.WriteLine("DatabaseProxy.DoWork");
                db.DoWork();
            }
            
            class InternalRM : IPromotableSinglePhaseNotification
            {
                #region IPromotableSinglePhaseNotification Members
                
                // This member will be called during the call to EnlistPromotableSinglePhase
                // The RM will usually allocate its internal transaction state here
                public void Initialize()
                {
                    System.Console.WriteLine("InternalRM.Initialize");
                }
                
                // This method will be called if the RM should Rollback the
                // transaction.  Note that this method will be called even if
                // the transaction has been promoted to a distributed transaction.
                public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
                {
                    System.Console.WriteLine("InternalRM.Rollback");
                    db.RollbackWork();
                    singlePhaseEnlistment.Aborted();
                }
                
                // This method will be called when the RM should Commit the
                // transaction.  Note that this method will be called even if
                // the transaction has actually been promoted to a distributed
                // transaction.
                public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
                {
                    System.Console.WriteLine("InternalRM.SinglePhaseCommit");
                    db.CommitWork();
                    singlePhaseEnlistment.Committed();
                }
                
                #endregion
            
                #region ITransactionPromoter Members
                
                // This method will be called if System.Transactions
                // determines that the transaction actually needs the support of
                // a fully distributed transaction manager.  The return value of
                // this method is a promoted representation of the transaction
                // usually in the form of transmitter/receiver propagation token
                public byte[] Promote()
                {
                    System.Console.WriteLine("InternalRm.Promote");
                    return db.Promote();
                }
                
                #endregion
            
                private PSPEServer.PSPEDatabaseServer db;
                
                public InternalRM(PSPEServer.PSPEDatabaseServer db)
                {
                    this.db = db;
                }
                
                public void Enlist()
                {
                    System.Console.WriteLine("InternalRM.Enlist");
                    if (null != Transaction.Current)
                    {
                        if (!Transaction.Current.EnlistPromotableSinglePhase(this))
                        {
                            System.Console.WriteLine("PSPE failed, doing regular Enlist");
                            // PSPE failed; we need to use the regular enlistment
                            db.Enlist(TransactionInterop.GetTransmitterPropagationToken(Transaction.Current));
                        }
                    }
                }
            }
        }
     
        class DurableRM : IEnlistmentNotification
        {
            
            #region IEnlistmentNotification Members
            
            public void Commit(Enlistment enlistment)
            {
                System.Console.WriteLine("DurableRM.Commit");
                enlistment.Done();
            }
            
            public void InDoubt(Enlistment enlistment)
            {
                System.Console.WriteLine("DurableRM.InDoubt");
                throw new Exception("The method or operation is not implemented.");
            }
            
            public void Prepare(PreparingEnlistment preparingEnlistment)
            {
                System.Console.WriteLine("DurableRM.Prepare");
                // first a durable RM will log preparingEnlistment.RecoveryInformation(), but this is just a sample
                preparingEnlistment.Prepared();
            }
            
            public void Rollback(Enlistment enlistment)
            {
                System.Console.WriteLine("DurableRM.Rollback");
                enlistment.Done();
            }
            
            #endregion
            
            public void OpenConnection()
            {
                System.Console.WriteLine("DurableRM.OpenConnection and enlist durable");
                if (null != Transaction.Current)
                {
                    Transaction.Current.EnlistDurable(Guid.NewGuid(), this, EnlistmentOptions.None);
                }
            }
            public void DoWork()
            {
                System.Console.WriteLine("DurableRM - DoWork");
            }
        }
    }
}
&lt;/PRE&gt;&amp;lt;/client.cs&amp;gt;&lt;BR&gt;&amp;lt;server.cs&amp;gt;&lt;BR&gt;&lt;PRE class=csharpcode&gt;using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Transactions;
using System.Diagnostics;
 
namespace PSPEServer
{
    class Server
    {
        static void Main(string[] args)
        {
            TcpChannel tcpChannel = new TcpChannel(8085);
            ChannelServices.RegisterChannel(tcpChannel);
            RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("PSPEServer.PSPEDatabaseServer"),
                "MyDatabase", WellKnownObjectMode.Singleton);
            System.Console.WriteLine("Server running...");
            System.Console.ReadLine();
        }
    }
 
    public class PSPEDatabaseServer : MarshalByRefObject
    {
        private int internalTxID = 0;
        private CommittableTransaction tx;
        private InternalServerRM internalServerRM;

        public int Connect()
        {
            System.Console.WriteLine("client connected");
            return ++internalTxID;
        }

        public void DoWork()
        {
            System.Console.WriteLine("PSPEDBServer.DoWork");
        }

        public byte[] Promote()
        {
            System.Console.WriteLine("PSPEDBServer.Promote");
            this.tx = new CommittableTransaction();
            Debug.Assert(this.internalServerRM == null);
            // the following statement will cause the transaction to be promoted to MSDTC
            byte[] txToken = TransactionInterop.GetTransmitterPropagationToken(this.tx);
            Enlist(txToken);
            return txToken;
        }

        public void CommitWork()
        {
            System.Console.WriteLine("PSPEDBServer.CommitWork");
            if (tx != null)
            {
                // we have a distributed transaction, and so we have to commit it
                tx.Commit();
            }
            else
            {
                // we only have an internal tx
                System.Console.WriteLine("committing internal tx:" + internalTxID);
            }
        }

        public void RollbackWork()
        {
            System.Console.WriteLine("PSPEDBServer.RollbackWork");
            if (tx != null)
            {
                // we have a distributed transaction, and so we have to rollback it
                tx.Rollback();
            }
            else
            {
                // we only have an internal tx
                System.Console.WriteLine("aborting internal tx:" + internalTxID);
            }        
        }

        public void Enlist(byte[] txToken)
        {
            System.Console.WriteLine("PSPEDBServer.Enlist");
            this.internalServerRM = new InternalServerRM();
            this.internalServerRM.Enlist(txToken);
        }

        private class InternalServerRM : ISinglePhaseNotification
        {

            #region ISinglePhaseNotification Members

            public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
            {
                System.Console.WriteLine("InternalServerRM.SPC");
                singlePhaseEnlistment.Committed();
            }

            #endregion

            #region IEnlistmentNotification Members

            public void Commit(Enlistment enlistment)
            {
                System.Console.WriteLine("InternalServerRM.Commit");
                enlistment.Done();
            }

            public void InDoubt(Enlistment enlistment)
            {
                System.Console.WriteLine("InternalServerRM.InDoubt");
                throw new Exception("The method or operation is not implemented.");
            }

            public void Prepare(PreparingEnlistment preparingEnlistment)
            {
                System.Console.WriteLine("InternalServerRM.Prepare");
                // first a durable RM will log preparingEnlistment.RecoveryInformation(), but this is just a sample
                preparingEnlistment.Prepared();
            }

            public void Rollback(Enlistment enlistment)
            {
                System.Console.WriteLine("InternalServerRM.Rollback");
                enlistment.Done();
            }

            #endregion

            private Guid rmGuid = new Guid("{B14FF9BB-8419-4dbc-A78C-3C1453D60AC4}");

            public void Enlist(byte[] txToken)
            {
                System.Console.WriteLine("InternalServerRM.Enlist");
                TransactionInterop.GetTransactionFromTransmitterPropagationToken(txToken).EnlistDurable(
                    this.rmGuid, this, EnlistmentOptions.None);
            }
        }
    }
}&lt;/PRE&gt;&amp;lt;/server.cs&amp;gt; 
&lt;P&gt;If we run the code, with only the PSPE enlistment, System.Transactions is managing the transaction and MSDTC is never involved; we get the following outputs:&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&amp;lt;client&amp;gt;&lt;BR&gt;DatabaseProxy.OpenConnection&lt;BR&gt;Internal tx id:1&lt;BR&gt;InternalRM.Enlist&lt;BR&gt;InternalRM.Initialize&lt;BR&gt;DatabaseProxy.DoWork&lt;BR&gt;InternalRM.SinglePhaseCommit&lt;BR&gt;&amp;lt;/client&amp;gt;&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&amp;lt;server&amp;gt;&lt;BR&gt;Server running...&lt;BR&gt;client connected&lt;BR&gt;PSPEDBServer.DoWork&lt;BR&gt;PSPEDBServer.CommitWork&lt;BR&gt;committing internal tx:1&lt;BR&gt;&amp;lt;/server&amp;gt;&lt;BR&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If we enlist a durable RM after the PSPE enlistment (uncomment section 2), the transaction is promoted to MSDTC when the durable enlistment is done and the outputs are:&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&amp;lt;client&amp;gt;&lt;BR&gt;DatabaseProxy.OpenConnection&lt;BR&gt;Internal tx id:1&lt;BR&gt;InternalRM.Enlist&lt;BR&gt;InternalRM.Initialize&lt;BR&gt;DatabaseProxy.DoWork&lt;BR&gt;DurableRM.OpenConnection and enlist durable&lt;BR&gt;InternalRm.Promote&lt;BR&gt;DurableRM - DoWork&lt;BR&gt;InternalRM.SinglePhaseCommit&lt;BR&gt;DurableRM.Prepare&lt;BR&gt;DurableRM.Commit&lt;BR&gt;&amp;lt;/client&amp;gt;&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&amp;lt;server&amp;gt;&lt;BR&gt;Server running...&lt;BR&gt;client connected&lt;BR&gt;PSPEDBServer.DoWork&lt;BR&gt;PSPEDBServer.Promote&lt;BR&gt;PSPEDBServer.Enlist&lt;BR&gt;InternalServerRM.Enlist&lt;BR&gt;PSPEDBServer.CommitWork&lt;BR&gt;InternalServerRM.Prepare&lt;BR&gt;InternalServerRM.Commit&lt;BR&gt;&amp;lt;/server&amp;gt;&lt;BR&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;And finally, if we enlist a durable RM before the PSPE enlistment (uncomment section 1), the PSPE enlistment will fail and thus the RM will have to follow the regular enlistment procedures. The outputs in this case are:&lt;/P&gt;
&lt;P&gt;&amp;lt;client&amp;gt;&lt;BR&gt;DurableRM.OpenConnection and enlist durable&lt;BR&gt;DurableRM - DoWork&lt;BR&gt;DatabaseProxy.OpenConnection&lt;BR&gt;Internal tx id:1&lt;BR&gt;InternalRM.Enlist&lt;BR&gt;PSPE failed, doing regular Enlist&lt;BR&gt;DatabaseProxy.DoWork&lt;BR&gt;DurableRM.Prepare&lt;BR&gt;DurableRM.Commit&lt;BR&gt;&amp;lt;/client&amp;gt;&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&amp;lt;server&amp;gt;&lt;BR&gt;Server running...&lt;BR&gt;client connected&lt;BR&gt;PSPEDBServer.Enlist&lt;BR&gt;InternalServerRM.Enlist&lt;BR&gt;PSPEDBServer.DoWork&lt;BR&gt;InternalServerRM.Prepare&lt;BR&gt;InternalServerRM.Commit&lt;BR&gt;&amp;lt;/server&amp;gt;&lt;BR&gt;&amp;nbsp;&lt;BR&gt;NOTE: the goal of my sample was to show the use of PSPE. For simplicity, I ignored dealing with the recovery (I will follow up with another post where I will target Durable Enlistments and recovery).&lt;BR&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=418595" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/florinlazar/archive/tags/MSDTC/default.aspx">MSDTC</category><category domain="http://blogs.msdn.com/florinlazar/archive/tags/Transactions/default.aspx">Transactions</category><category domain="http://blogs.msdn.com/florinlazar/archive/tags/promotable+transactions/default.aspx">promotable transactions</category><category domain="http://blogs.msdn.com/florinlazar/archive/tags/PSPE/default.aspx">PSPE</category></item></channel></rss>