async Task Commit(bool async) { CheckReady(); using (_connector.StartUserAction()) { Log.Debug("Committing transaction", _connector.Id); await _connector.ExecuteInternalCommand(PregeneratedMessage.CommitTransaction, async); Clear(); } }
public void Prepare(PreparingEnlistment preparingEnlistment) { CheckDisposed(); Debug.Assert(_transaction != null, "No transaction"); Debug.Assert(_localTx != null, "No local transaction"); Debug.Assert(_connector != null, "No connector"); Log.Debug($"Two-phase transaction prepare (localid={_txId})", _connector.Id); // The PostgreSQL prepared transaction name is the distributed GUID + our connection's process ID, for uniqueness _preparedTxName = $"{_transaction.TransactionInformation.DistributedIdentifier}/{_connector.BackendProcessId}"; try { using (_connector.StartUserAction()) _connector.ExecuteInternalCommand($"PREPARE TRANSACTION '{_preparedTxName}'"); // The MSDTC, which manages escalated distributed transactions, performs the 2nd phase // asynchronously - this means that TransactionScope.Dispose() will return before all // resource managers have actually commit. // If the same connection tries to enlist to a new TransactionScope immediately after // disposing an old TransactionScope, its EnlistedTransaction must have been cleared // (or we'll throw a double enlistment exception). This must be done here at the 1st phase // (which is sync). if (_connector.Connection != null) { _connector.Connection.EnlistedTransaction = null; } preparingEnlistment.Prepared(); } catch (Exception e) { Dispose(); preparingEnlistment.ForceRollback(e); } }