Possible optimization:TransactionScope 2 - 2.5 tx/sec gain

Discussion of open issues, suggestions and bugs regarding ADO.NET provider for PostgreSQL
Post Reply
bpipe
Posts: 19
Joined: Mon 08 Oct 2012 12:14

Possible optimization:TransactionScope 2 - 2.5 tx/sec gain

Post by bpipe » Wed 20 Feb 2013 06:57

I'm not yet sure if it's a bug (now working code), or you have missed optimization opportunity (no such code at all).

Currently your driver when enlisting in TransactionScope always do two phase commit, even when it is the only participant of transaction, PREPARE TRANSACTION; COMMIT PREPARED is executed on successful transaction end.

I did some benchmarks, and using TransactionScope is 2-2.5 times slower then using Connection.BeginTransaction() due to always doing 2PC with PREPARE TRANSACTION.

I guess your are always enlisting with Transaction.EnlistVolatile(IEnlistmentNotification) which will always call prepare then commit, when you could do Transaction.EnlistVolatile(ISinglePhaseNotification) or EnlistPromotableSinglePhase

Code: Select all

        [TestMethod]
        public void TX()
        {
            using (TransactionScope ts = new TransactionScope())
            {
                PgSqlConnection connection = new PgSqlConnection("...;Enlist=true;Unicode=true");
                connection.Open();
                ts.Complete();
            }
        }

bpipe
Posts: 19
Joined: Mon 08 Oct 2012 12:14

Re: Possible optimization:TransactionScope 2 - 2.5 tx/sec gain

Post by bpipe » Wed 20 Feb 2013 07:31

Actually I'm quite intrigued why it works this way, I have read your source code (decompiled) and it should work correctly.
DbConnectionInternal.cs

Code: Select all

        this.l = new WeakReference((object) transaction);
        x A_1 = this.b(transaction);
        if (!this.TwoPhaseCommitSupported)
        {
          if (ar.d(transaction))
            throw new InvalidOperationException("Cannot enlist local transaction, because current global transaction already contains distributed transactions.");
          this.j = Guid.Empty;
          DbConnectionInternal.a(transaction, A_1);
        }
        else if (!this.SimulateTwoPhaseCommit)
        {
          this.j = DbConnectionInternal.a(transaction);
          transaction.EnlistVolatile((IEnlistmentNotification) A_1, EnlistmentOptions.None);
        }
        else
        {
          this.j = Guid.Empty;
          transaction.EnlistVolatile((ISinglePhaseNotification) A_1, EnlistmentOptions.None);
        }
TwoPhaseCommitSupported is set to constant False in code, so this should always be executed:
private static void a(Transaction A_0, x A_1)
{
if (!A_0.EnlistPromotableSinglePhase((IPromotableSinglePhaseNotification) A_1))
throw new InvalidOperationException("Cannot enlist transaction. Possibly single phase transaction was already used.");
}
But I can clearly see from Transaction tracelog that EnlistVolatile is used. The only guess I have is that TwoPhaseCommitSupported is changed somewhere externally, but I was unable to find that spot.

Yeah I have checked at runtime TwoPhaseCommitSupported = true and SimulateThwoPhaseCommit is false, so are always doing transaction.EnlistVolatile((IEnlistmentNotification) which uses prepare then commit.

Actually the logic behind this is strange for me. I think you need to remove ! at public void EnlistToDistributedTransactionInternal(Transaction transaction)

Code: Select all

if (!this.TwoPhaseCommitSupported)

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Re: Possible optimization:TransactionScope 2 - 2.5 tx/sec gain

Post by Shalex » Thu 21 Feb 2013 17:26

Please try using the "TransactionScopeLocal=true;" connection string parameter when working with the TransactionScope class. This should improve performance.

If there are several connections with the same connection string (which includes "Transaction Scope Local=true;") within a scope of TransactionScope, our provider will use only one connection internally. The default value is false.

bpipe
Posts: 19
Joined: Mon 08 Oct 2012 12:14

Re: Possible optimization:TransactionScope 2 - 2.5 tx/sec gain

Post by bpipe » Fri 22 Feb 2013 07:29

No this will not help.
Transaction Scope Local=true; will work only with single transaction enlistment.

What I'm trying to say is that there is no need for Two phase commit when there is only one Transaction Resource in transaction.

Also "Transaction Scope Local=true" leads to lots of confusion, because it has nothing to do with DTC, you driver doesn't use EnlistDurable, only durable resources use DTC.
What "Transaction Scope Local=true" mean in your driver is basically "allow only one resource manager in transaction", throw exception otherwise.

What you need to do is:
Transaction.EnlistVolatile((ISinglePhaseNotification)new RM())
When it is the only one RM in transaction .NET will call SinglePhaseCommit() where you do "COMMIT"
When it's not the only one .NET will call Prepare (where you do PREPARE TRANSACTION) then it will call Commit, where you do COMMIT PREPARED;

That's all. And this will be Two phase commit (if required). It will not use MSDTC.
The rule is simple to MSDTC goes only multiple DURABLE enlistments. Correctly (with recovery) implementing durable enlistment is hard.

What will be useful is Durable Enlistment=true option, which will control how RM is enlisted, but most people do not need durable enlistment (they do not even know how to recover it :-)

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Re: Possible optimization:TransactionScope 2 - 2.5 tx/sec gain

Post by Shalex » Tue 26 Feb 2013 09:38

Thank you for your suggestion. We will investigate the question and post here about the result.

Post Reply