public async Task CommitAsync(Enlistment enlistment) { using (await this.syncObject.LockAsync().Await()) { try { Tracer.Debug("Commit notification received for TX id: " + this.TransactionId); if (this.TransactionId != null) { // Now notify the broker that a new XA'ish transaction has completed. TransactionInfo info = new TransactionInfo(); info.ConnectionId = this.connection.ConnectionId; info.TransactionId = this.TransactionId; info.Type = (int)TransactionType.CommitTwoPhase; await this.connection.CheckConnectedAsync().Await(); await this.connection.SyncRequestAsync(info).Await(); Tracer.Debug("Transaction Commit Done TX id: " + this.TransactionId); RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); // if server responds that nothing needs to be done, then reply done. enlistment.Done(); AfterCommit(); } } catch (Exception ex) { Tracer.DebugFormat("Transaction[{0}] Commit failed with error: {1}", this.TransactionId, ex.Message); try { this.connection.OnAsyncException(ex); } catch (Exception error) { Tracer.Error(error.ToString()); } } finally { this.currentEnlistment = null; this.TransactionId = null; this.netTxState = TxState.None; CountDownLatch latch = this.recoveryComplete; if (latch != null) { latch.countDown(); } this.dtcControlEvent.Set(); } } }
/// <summary> /// Should be called from NetTxSession when created to check if any TX /// data is stored for recovery and whether the Broker has matching info /// stored. If an Transaction is found that belongs to this client and is /// still alive on the Broker it will be recovered, otherwise the stored /// data should be cleared. /// </summary> public void InitializeDtcTxContext() { // initialize the logger with the current Resource Manager Id RecoveryLogger.Initialize(ResourceManagerId); KeyValuePair <XATransactionId, byte[]>[] localRecoverables = RecoveryLogger.GetRecoverables(); if (localRecoverables.Length == 0) { Tracer.Debug("Did not detect any open DTC transaction records on disk."); // No local data so anything stored on the broker can't be recovered here. return; } XATransactionId[] recoverables = TryRecoverBrokerTXIds(); if (recoverables.Length == 0) { Tracer.Debug("Did not detect any recoverable transactions at Broker."); // Broker has no recoverable data so nothing to do here, delete the // old recovery log as its stale. RecoveryLogger.Purge(); return; } List <KeyValuePair <XATransactionId, byte[]> > matches = new List <KeyValuePair <XATransactionId, byte[]> >(); foreach (XATransactionId recoverable in recoverables) { foreach (KeyValuePair <XATransactionId, byte[]> entry in localRecoverables) { if (entry.Key.Equals(recoverable)) { Tracer.DebugFormat("Found a matching TX on Broker to stored Id: {0} reenlisting.", entry.Key); matches.Add(entry); } } } if (matches.Count != 0) { this.recoveryComplete = new CountDownLatch(matches.Count); foreach (KeyValuePair <XATransactionId, byte[]> recoverable in matches) { this.transactionId = recoverable.Key; Tracer.Info("Reenlisting recovered TX with Id: " + this.transactionId); this.currentEnlistment = TransactionManager.Reenlist(ResourceManagerGuid, recoverable.Value, this); } this.recoveryComplete.await(); Tracer.Debug("All Recovered TX enlistments Reports complete, Recovery Complete."); TransactionManager.RecoveryComplete(ResourceManagerGuid); return; } // The old recovery information doesn't match what's on the broker so we // should discard it as its stale now. RecoveryLogger.Purge(); }
public async Task InDoubtAsync(Enlistment enlistment) { using (await syncObject.LockAsync().Await()) { try { Tracer.Debug("In Doubt notification received for TX id: " + this.TransactionId); await BeforeEndAsync().Await(); // Now notify the broker that Rollback should be performed. TransactionInfo info = new TransactionInfo(); info.ConnectionId = this.connection.ConnectionId; info.TransactionId = this.TransactionId; info.Type = (int)TransactionType.End; await this.connection.CheckConnectedAsync().Await(); await this.connection.SyncRequestAsync((TransactionInfo)info.Clone()).Await(); info.Type = (int)TransactionType.Rollback; await this.connection.CheckConnectedAsync().Await(); await this.connection.SyncRequestAsync(info).Await(); Tracer.Debug("InDoubt Transaction Rollback Done TX id: " + this.TransactionId); RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); // if server responds that nothing needs to be done, then reply done. enlistment.Done(); AfterRollback(); } finally { this.currentEnlistment = null; this.TransactionId = null; this.netTxState = TxState.None; CountDownLatch latch = this.recoveryComplete; if (latch != null) { latch.countDown(); } this.dtcControlEvent.Set(); } } }
public void InDoubt(Enlistment enlistment) { try { dtcControlEvent.Reset(); Tracer.Debug("In Doubt notification received for TX id: " + this.transactionId); BeforeEnd(); // Now notify the broker that Rollback should be performed. TransactionInfo info = new TransactionInfo(); info.ConnectionId = this.connection.ConnectionId; info.TransactionId = this.transactionId; info.Type = (int)TransactionType.End; this.connection.CheckConnected(); this.connection.SyncRequest(info); info.Type = (int)TransactionType.Rollback; this.connection.CheckConnected(); this.connection.SyncRequest(info); Tracer.Debug("InDoubt Transaction Rollback Done TX id: " + this.transactionId); RecoveryLogger.LogRecovered(this.transactionId as XATransactionId); // if server responds that nothing needs to be done, then reply done. enlistment.Done(); AfterRollback(); } finally { this.currentEnlistment = null; this.transactionId = null; CountDownLatch latch = this.recoveryComplete; if (latch != null) { latch.countDown(); } this.dtcControlEvent.Set(); } }
public async Task PrepareAsync(PreparingEnlistment preparingEnlistment) { using (await this.syncObject.LockAsync().Await()) { this.netTxState = TxState.Pending; try { Tracer.Debug("Prepare notification received for TX id: " + this.TransactionId); await BeforeEndAsync().Await(); // Before sending the request to the broker, log the recovery bits, if // this fails we can't prepare and the TX should be rolled back. RecoveryLogger.LogRecoveryInfo(this.TransactionId as XATransactionId, preparingEnlistment.RecoveryInformation()); // Inform the broker that work on the XA'sh TX Branch is complete. TransactionInfo info = new TransactionInfo(); info.ConnectionId = this.connection.ConnectionId; info.TransactionId = this.TransactionId; info.Type = (int)TransactionType.End; await this.connection.CheckConnectedAsync().Await(); await this.connection.SyncRequestAsync((TransactionInfo)info.Clone()).Await(); // Prepare the Transaction for commit. info.Type = (int)TransactionType.Prepare; IntegerResponse response = (IntegerResponse)await this.connection.SyncRequestAsync(info).Await(); if (response.Result == XA_READONLY) { Tracer.Debug("Transaction Prepare done and doesn't need a commit, TX id: " + this.TransactionId); this.TransactionId = null; this.currentEnlistment = null; // Read Only means there's nothing to recover because there was no // change on the broker. RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); // if server responds that nothing needs to be done, then reply done. // otherwise the DTC will call Commit or Rollback but another transaction // can already be in progress and this one would be commited or rolled back // immediately. preparingEnlistment.Done(); // Done so commit won't be called. AfterCommit(); // A Read-Only TX is considered closed at this point, DTC won't call us again. this.dtcControlEvent.Set(); } else { Tracer.Debug("Transaction Prepare succeeded TX id: " + this.TransactionId); // If work finished correctly, reply prepared preparingEnlistment.Prepared(); } } catch (Exception ex) { Tracer.DebugFormat("Transaction[{0}] Prepare failed with error: {1}", this.TransactionId, ex.Message); AfterRollback(); preparingEnlistment.ForceRollback(); try { this.connection.OnAsyncException(ex); } catch (Exception error) { Tracer.Error(error.ToString()); } this.currentEnlistment = null; this.TransactionId = null; this.netTxState = TxState.None; this.dtcControlEvent.Set(); } } }
public void Rollback(Enlistment enlistment) { lock (this.syncObject) { try { Tracer.Debug("Rollback notification received for TX id: " + this.transactionId); if (this.transactionId != null) { BeforeEnd(); // Now notify the broker that a new XA'ish transaction has started. TransactionInfo info = new TransactionInfo(); info.ConnectionId = this.connection.ConnectionId; info.TransactionId = this.transactionId; info.Type = (int)TransactionType.End; this.connection.CheckConnected(); this.connection.SyncRequest(info); info.Type = (int)TransactionType.Rollback; this.connection.CheckConnected(); this.connection.SyncRequest(info); Tracer.Debug("Transaction Rollback Done TX id: " + this.transactionId); RecoveryLogger.LogRecovered(this.transactionId as XATransactionId); // if server responds that nothing needs to be done, then reply done. enlistment.Done(); AfterRollback(); } } catch (Exception ex) { Tracer.DebugFormat("Transaction[{0}] Rollback failed with error: {1}", this.transactionId, ex.Message); AfterRollback(); try { this.connection.OnException(ex); } catch (Exception error) { Tracer.Error(error.ToString()); } } finally { this.currentEnlistment = null; this.transactionId = null; this.netTxState = TxState.None; CountDownLatch latch = this.recoveryComplete; if (latch != null) { latch.countDown(); } this.dtcControlEvent.Set(); } } }
public void Prepare(PreparingEnlistment preparingEnlistment) { try { dtcControlEvent.Reset(); Tracer.Debug("Prepare notification received for TX id: " + this.transactionId); BeforeEnd(); // Before sending the request to the broker, log the recovery bits, if // this fails we can't prepare and the TX should be rolled back. RecoveryLogger.LogRecoveryInfo(this.transactionId as XATransactionId, preparingEnlistment.RecoveryInformation()); // Inform the broker that work on the XA'sh TX Branch is complete. TransactionInfo info = new TransactionInfo(); info.ConnectionId = this.connection.ConnectionId; info.TransactionId = this.transactionId; info.Type = (int)TransactionType.End; this.connection.CheckConnected(); this.connection.SyncRequest(info); // Prepare the Transaction for commit. info.Type = (int)TransactionType.Prepare; IntegerResponse response = (IntegerResponse)this.connection.SyncRequest(info); if (response.Result == XA_READONLY) { Tracer.Debug("Transaction Prepare done and doesn't need a commit, TX id: " + this.transactionId); this.transactionId = null; this.currentEnlistment = null; // Read Only means there's nothing to recover because there was no // change on the broker. RecoveryLogger.LogRecovered(this.transactionId as XATransactionId); // if server responds that nothing needs to be done, then reply prepared // but clear the current state data so we appear done to the commit method. preparingEnlistment.Prepared(); // Done so commit won't be called. AfterCommit(); } else { Tracer.Debug("Transaction Prepare succeeded TX id: " + this.transactionId); // If work finished correctly, reply prepared preparingEnlistment.Prepared(); } } catch (Exception ex) { Tracer.DebugFormat("Transaction[{0}] Prepare failed with error: {1}", this.transactionId, ex.Message); AfterRollback(); preparingEnlistment.ForceRollback(); try { this.connection.OnException(ex); } catch (Exception error) { Tracer.Error(error.ToString()); } this.currentEnlistment = null; this.transactionId = null; } finally { this.dtcControlEvent.Set(); } }