private void Rollback() { var list = this.ResouceManagers.ResourceManagers; for (int i = 0; i < list.Count; i++) { RM r = list[i]; string outstring = string.Format("2PC:Rollback {0}:{1}\r", this.Context.Id, this.ResouceManagers.RMNames[i]); this.Message += outstring; Console.Write(outstring); int temp = i; ThreadPool.QueueUserWorkItem(o => { while (true) { try { r.Abort(this.Context); break; } catch (WebException) { } Console.WriteLine("ResourceManager {0} is not ready, wait 0.5 second", this.ResouceManagers.RMNames[temp]); Thread.Sleep(500); } // presume abort, if we don't check return of the abort // Once the abort message is sent, it is consider done. this.SetState(temp, CommitState.Rollbacked); }); } }
// This function reviews all outstanding transaction and reissues Commit() or Abort() to the // unackowledged RMs public void recovery() { Console.Out.WriteLine("Recovery started..."); List <string> deleteTransactionList = new List <string>(); // Keep track of transactions that are fully acknowledged by all its RMs lock (OutstandingTransactions) { if (OutstandingTransactions.transactionList.Keys.Count == 0) { Console.WriteLine("Recovery: No outstanding transactions to recover. Recovery completed."); return; } foreach (string transactionId in OutstandingTransactions.transactionList.Keys) { // For each outstanding transaction, get its transaction id and its OutstandingTransactions value OutstandingTransactions.OutstandingTransactionsValue entry = OutstandingTransactions.transactionList[transactionId]; Transaction context = new Transaction(); context.Id = new Guid(transactionId); Console.Out.WriteLine(string.Format("Recovery: Recovering transaction {0}...", transactionId)); // For each of the remaining NACK RMs, reissue the Commit or Abort for (int i = 0; i < entry.nackRMList.Count; ++i) { string rmName = entry.nackRMList[i]; RM rm = GetResourceMananger(rmName); if (rm == null) { Console.Out.WriteLine(string.Format("Recovery: Failed to find resource manager {0}", rmName)); } else { ExecuteActionWithTimeout exec; if (entry.transactionType == OutstandingTransactions.OutstandingTransactionsValue.TransactionType.Commit) { Console.Out.WriteLine(string.Format("Recovery: Re-committing resource manager {0}...", rmName)); exec = new ExecuteActionWithTimeout(rmName, () => rm.Commit(context)); } else { Console.Out.WriteLine(string.Format("Recovery: Re-aborting resource manager {0}...", rmName)); exec = new ExecuteActionWithTimeout(rmName, () => rm.Abort(context)); } // Execute the commit or abort try { exec.Run(); Console.Out.WriteLine("Recovery: Successful!"); // Remove RM from the current transaction since we received the Done message (ie the call didn't time out) OutstandingTransactions.transactionList[transactionId].nackRMList.RemoveAt(i); --i; } catch (TimeoutException) { System.Console.WriteLine("Recovery: Failed!"); } } } // PRESUMED ABORT: If the transaction is Abort, we don't care about the Done message and we forget about // the transaction immediately. // For commit transactions, mark transaction for removal if we received Done from all RMs if (entry.transactionType == OutstandingTransactions.OutstandingTransactionsValue.TransactionType.Abort || entry.nackRMList.Count == 0) { deleteTransactionList.Add(transactionId); } } // Delete transaction marked for removal from the outstanding transaction list foreach (string s in deleteTransactionList) { OutstandingTransactions.transactionList.Remove(s); } // Update file to reflect outstanding transactions status // This is a full rewrite of the outstanding transaction file and hence it will implicitly garbage collect // the transactions that are no longer outstanding. OutstandingTransactions.WriteToFile(); } Console.Out.WriteLine("Recovery completed."); }