/// <summary> // Call from WC in response to a client's abort /// </summary> /// <param name="context"></param> public void Abort(TP.Transaction context) { // Get the list of names of the resource manager involved in this transaction List <string> rmNameList = GetRMListForTransaction(context); if (null == rmNameList) { System.Console.WriteLine(string.Format("Transaction {0} unknown", context.Id)); } // Get the list of resource managers involved in this transaction List <RM> rmList = new List <RM>(); lock (this.resourceManagers) { foreach (string name in rmNameList) { RM manager = GetResourceMananger(name); if (null == manager) { continue; } rmList.Add(manager); } // Write transaction id and list of RM to outstanding transaction list before aborting the transaction // Flush the entry to the outstanding transaction file immediately OutstandingTransactions.OutstandingTransactionsValue abortedTransactionValue = new OutstandingTransactions.OutstandingTransactionsValue( OutstandingTransactions.OutstandingTransactionsValue.TransactionType.Abort, rmNameList); lock (OutstandingTransactions) { OutstandingTransactions.UpdateAndFlush(context.Id.ToString(), abortedTransactionValue); System.Console.WriteLine(string.Format("Transaction {0} aborting...", context.Id)); for (int i = 0; i < rmList.Count; ++i) { ExecuteActionWithTimeout exec = new ExecuteActionWithTimeout(rmList[i].GetName(), () => rmList[i].Abort(context)); try { exec.Run(); } catch (TimeoutException) { System.Console.WriteLine(string.Format("Transaction {0} timed out while waiting for RM {1} to abort...", context.Id, rmList[i].GetName())); } } // PRESUMED ABORT: Once the TM sends out the abort, it forgets about the transaction immediately. // So we write transaction id and an _empty_ list of unacknowledged RMs to the outstanding transaction list. // This will cancel out the entry we logged earlier about the transaction and all its RMs. // Flush the entry to the outstanding transaction file immediately abortedTransactionValue.nackRMList.Clear(); OutstandingTransactions.UpdateAndFlush(context.Id.ToString(), abortedTransactionValue); if (abortedTransactionValue.nackRMList.Count == 0) { System.Console.WriteLine(string.Format("Transaction {0} aborted", context.Id)); } else { System.Console.WriteLine(string.Format("Transaction {0} aborted with some timeouts", context.Id)); } } } }
/// <summary> // Call from WC in response to a client's commit. /// </summary> /// <param name="context"></param> public void Commit(TP.Transaction context) { // Get the list of names of the resource manager involved in this transaction List <string> rmNameList = GetRMListForTransaction(context); if (null == rmNameList) { System.Console.WriteLine(string.Format("Transaction {0} unknown", context.Id)); throw new AbortTransationException(); } // Get the list of resource managers involved in this transaction List <RM> rmList = new List <RM>(); lock (this.resourceManagers) { foreach (string name in rmNameList) { RM manager = GetResourceMananger(name); if (null == manager) { continue; } rmList.Add(manager); } // Request to prepare for all resource managers involved in this transaction bool allPrepared = true; List <ExecuteFuncWithTimeout <bool> > execPrepareList = new List <ExecuteFuncWithTimeout <bool> >(); try { // Execute the Prepare() function for each associated RMs with timeout for (int i = 0; i < rmList.Count; ++i) { ExecuteFuncWithTimeout <bool> exec = new ExecuteFuncWithTimeout <bool>(rmList[i].GetName(), () => rmList[i].Prepare(context)); execPrepareList.Add(exec); exec.Run(); } } // If a timeout occurs, it means that RMs are not all prepared and we should abort // the transaction catch (TimeoutException) { System.Console.WriteLine(string.Format("Transaction {0} timed out while waiting for RequestToPrepare. Aborting transaction...", context.Id)); allPrepared = false; } if (allPrepared) { // If all resource managers are ready responded to the request to prepare, check if // any of them responded NO to the request. foreach (ExecuteFuncWithTimeout <bool> exec in execPrepareList) { if (exec.completed && exec.result == false) { allPrepared = false; System.Console.WriteLine(string.Format("Transaction {0} received No for RequestToPrepare. Aborting transaction...", context.Id)); break; } } } execPrepareList.Clear(); // Initialize the list of RM to outstanding transaction list in case of recovery OutstandingTransactions.OutstandingTransactionsValue committedTransactionValue = new OutstandingTransactions.OutstandingTransactionsValue( OutstandingTransactions.OutstandingTransactionsValue.TransactionType.Commit, rmNameList); lock (OutstandingTransactions) { // If all resource managers responded Prepared to the Request to prepare, commit the transaction. if (allPrepared) { // Write transaction id and list of RM to outstanding transaction list before committing the transaction // Flush the entry to the outstanding transaction file immediately OutstandingTransactions.UpdateAndFlush(context.Id.ToString(), committedTransactionValue); System.Console.WriteLine(string.Format("Transaction {0} received Prepared from all resource managers. Committing transaction...", context.Id)); // Execute commit() in all associated RMs for (int i = 0; i < rmList.Count; ++i) { ExecuteActionWithTimeout exec = new ExecuteActionWithTimeout(rmList[i].GetName(), () => rmList[i].Commit(context)); try { exec.Run(); // Remove RM from list of RM when we received Done (ie no timeout has occurred) committedTransactionValue.nackRMList.Remove(rmList[i].GetName()); } catch (TimeoutException) { System.Console.WriteLine(string.Format("Transaction {0} timed out while waiting for RM {1} to commit...", context.Id, rmList[i].GetName())); } } // Write transaction id and list of unacknowledged RMs to the outstanding transaction list. // Flush the entry to the outstanding transaction file immediately OutstandingTransactions.UpdateAndFlush(context.Id.ToString(), committedTransactionValue); if (committedTransactionValue.nackRMList.Count == 0) { System.Console.WriteLine(string.Format("Transaction {0} commited", context.Id)); } else { System.Console.WriteLine(string.Format("Transaction {0} commited with some timeouts", context.Id)); } } else { // Initialize the list of RM to outstanding transaction list in case of recovery // Write transaction id and list of RM to outstanding transaction list before aborting the transaction // Flush the entry to the outstanding transaction file immediately committedTransactionValue.transactionType = OutstandingTransactions.OutstandingTransactionsValue.TransactionType.Abort; OutstandingTransactions.UpdateAndFlush(context.Id.ToString(), committedTransactionValue); System.Console.WriteLine(string.Format("Transaction {0} aborting...", context.Id)); // Execute abort() in all associated RMs for (int i = 0; i < rmList.Count; ++i) { ExecuteActionWithTimeout exec = new ExecuteActionWithTimeout(rmList[i].GetName(), () => rmList[i].Abort(context)); try { exec.Run(); } catch (TimeoutException) { System.Console.WriteLine(string.Format("Transaction {0} timed out while waiting for RM {1} to abort...", context.Id, rmList[i].GetName())); } } // PRESUMED ABORT: Once the TM sends out the abort, it forgets about the transaction immediately. // So we write transaction id and an _empty_ list of unacknowledged RMs to the outstanding transaction list. // This will cancel out the entry we logged earlier about the transaction and all its RMs. // Flush the entry to the outstanding transaction file immediately committedTransactionValue.nackRMList.Clear(); OutstandingTransactions.UpdateAndFlush(context.Id.ToString(), committedTransactionValue); if (committedTransactionValue.nackRMList.Count == 0) { System.Console.WriteLine(string.Format("Transaction {0} aborted", context.Id)); } else { System.Console.WriteLine(string.Format("Transaction {0} aborted with some timeouts", context.Id)); } } } } }