none
Promote single phase transaction RRS feed

  • Question

  • I have implemented support for durable enlistment of transactions (System.Transactions.Transaction.EnlistDurable) with an XA compliant resource manager. I have then proceeded to implement support for promotable single phase enlistment. My initial concern was that making a local transaction turn into an XA transaction would be my greatest hurdle. Unfortunately that was not so. 
    Instead it turns out that it does not seem possible to implement the ITransactionPromoter.Promote method in any reasonable way against an XA resource manager? The promote method expects a transmitter propagation token to come from another process. In my case that would require the resource manager to be on a Windows platform which is not always true. I want to do the promote in the same process and reach the same state as when I do two durable enlistments (which works fine).

    Has anyone figured out how to do this? I have tried various things like:

    byte[] ITransactionPromoter.Promote()
    {
        var tx = new CommittableTransaction();
        byte[] txToken = TransactionInterop.GetTransmitterPropagationToken(tx);
        return txToken;
    }

    This gives exception System.InvalidOperationException: The transaction returned from Promote already exists as a
    distributed transaction.

    Any help is truly appreciated!

    PS. I really like the structure of the TransactionScope class and the thought behind it. It is a pity if it is not 
    possible to integrate access with it to allow fast interaction between both Microsoft and non-Microsoft resources.


    Bengt

    Tuesday, April 11, 2017 12:52 PM

All replies

  • Hi Bengt G,

    Could you please provide a simple complete code, which I could reproduce the issue?

    Best regards,

    Cole Wu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, April 13, 2017 7:45 AM
    Moderator
  • Hi Cole Wu!

    Here is sample stripped of everything except essentials for reproducing the problem:

    using System;
    using System.Transactions;
    
    namespace PromotableSinglePhaseReproducer
    {
        class Program
        {
            static void Main(string[] args)
            {
                //
                //  Main program acts as application
                //
                using (TransactionScope transcope = new TransactionScope())
                {
                    //
                    //  Access first resource manager
                    //
                    using (RmClient rm1 = new RmClient())
                    {
                        rm1.Open();
                    }
                    //
                    //  Access second resource manager. This will force promotion
                    //
                    using (RmClient rm2 = new RmClient())
                    {
                        rm2.Open();
                    }
                    transcope.Complete();
                }
            }
        }
        class RmClient : IDisposable
        {
            public RmClient()
            { }
            public void Open()
            {
                if (Transaction.Current != null)
                {
                    //
                    //  Caller is inside TransactionScope
                    //
                    PSPCallback pspc = new PSPCallback();
                    if (!Transaction.Current.EnlistPromotableSinglePhase(pspc))
                    {
                        //
                        //  Single phase enlistment failed. EnlistDurable will invoke single phase Promote which fails
                        //
                        Guid g = new Guid("EFCADF70-54C3-4550-8429-D378FE6D4EE3");
                        TPCallback tpc = new TPCallback();
                        //
                        //  In my original code I also call things like GetDtcTransaction which promotes the transaction as well.
                        //
                        //var dtcTrans = TransactionInterop.GetDtcTransaction(Transaction.Current);
                        //...
                        //
                        Transaction.Current.EnlistDurable(g, tpc, EnlistmentOptions.None);
                    }
                }
            }
    
            #region IDisposable Support
            private bool disposedValue = false; // To detect redundant calls
    
            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                    }
                    disposedValue = true;
                }
            }
    
            void IDisposable.Dispose()
            {
                Dispose(true);
            }
            #endregion
        }
        class PSPCallback : IPromotableSinglePhaseNotification
        {
            public void Initialize()
            {
            }
    
            public byte[] Promote()
            {
                //
                //  This is the crucial piece of code... What should/can be done here????
                //
                CommittableTransaction tx = new CommittableTransaction();
                byte[] txToken = TransactionInterop.GetTransmitterPropagationToken(tx);
                return txToken;
            }
    
            public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
            {
                singlePhaseEnlistment.Done();
            }
    
            public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
            {
                singlePhaseEnlistment.Done();
            }
        }
        class TPCallback : IEnlistmentNotification
        {
            void IEnlistmentNotification.Commit(Enlistment enlistment)
            {
                enlistment.Done();
            }
    
            void IEnlistmentNotification.InDoubt(Enlistment enlistment)
            {
                enlistment.Done();
            }
    
            void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
            {
                preparingEnlistment.Prepared();
            }
    
            void IEnlistmentNotification.Rollback(Enlistment enlistment)
            {
                enlistment.Done();
            }
        }
    }
    


    Bengt

    Thursday, April 13, 2017 1:45 PM
  • If you comment out the line:

                    //if (!Transaction.Current.EnlistPromotableSinglePhase(pspc))
    then the application runs to end and each prepare/commit is called for each of the two connections. I want the promotable single phase commit to behave in the same way (except that the first one will have a SinglePhaseCommit (with the class PSPCallback) instead of prepare/commit (with the class TPCallback)).


    Bengt

    Thursday, April 13, 2017 2:47 PM
  • Hi Bengt G,

    Please check the following blog. which mentioned.

    >>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).

    https://blogs.msdn.microsoft.com/florinlazar/2005/05/17/writing-a-resource-manager-that-supports-promotable-transactions-or-promotable-single-phase-enlistment-aka-pspe-in-system-transactions/

    Best regards,

    Cole Wu


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, April 20, 2017 6:39 AM
    Moderator
  • Thank you for your reply. 

    You say: "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)"

    In my example that I gave you we run into case 2) (when we call rm2.open). In this case the sample calls DurableEnlist just as specified! When this is done the Promote method is called. The question is what this method should do?

    I have, of course, studied the blog you refer to. The problem is that the blog assumes you have MSDTC on the resource manager side. In my case that is not true. I have an XA resource manager on any platform so I cannot "marshal the transaction to the RM and enlist".

    If you remove the single phase enlistment call in my sample you can do two DurableEnlist and then everything works. But I want to get to the same state but with one EnlistPromotableSinglePhase and one DurableEnlist. So how can I enlist in the Promote method in the same process and return a propagation token that will be accepted?

    Best Regards


    Bengt

    Thursday, April 20, 2017 9:32 AM