diff options
author | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2018-04-16 19:27:18 -0400 |
---|---|---|
committer | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2018-04-16 19:27:18 -0400 |
commit | 239f4fac258a3b973b3cb3187d2b175f757f84df (patch) | |
tree | 21ec075d8f14696bfa37a5236df1527aadd0227a /jstests/concurrency/fsm_workload_helpers | |
parent | 9a27a0fd9668231601d4d6cdb324eece306b91d1 (diff) | |
download | mongo-239f4fac258a3b973b3cb3187d2b175f757f84df.tar.gz |
SERVER-34293 Add test for atomicity and isolation of transactions.
Also adds a helper function for running a function inside of a
transaction and automatically retrying until it either succeeds or the
server returns a non-WriteConflict error response.
Diffstat (limited to 'jstests/concurrency/fsm_workload_helpers')
-rw-r--r-- | jstests/concurrency/fsm_workload_helpers/auto_retry_transaction.js | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/jstests/concurrency/fsm_workload_helpers/auto_retry_transaction.js b/jstests/concurrency/fsm_workload_helpers/auto_retry_transaction.js new file mode 100644 index 00000000000..f98e7d796e9 --- /dev/null +++ b/jstests/concurrency/fsm_workload_helpers/auto_retry_transaction.js @@ -0,0 +1,68 @@ +'use strict'; + +var {withTxnAndAutoRetryOnWriteConflict} = (function() { + + /** + * Calls 'func' with the print() function overridden to be a no-op. + * + * This function is useful for silencing JavaScript backtraces that would otherwise be logged + * from doassert() being called, even when the JavaScript exception is ultimately caught and + * handled. + */ + function quietly(func) { + const printOriginal = print; + try { + print = Function.prototype; + func(); + } finally { + print = printOriginal; + } + } + + /** + * Runs 'func' inside of a transaction started with 'txnOptions', and automatically retries + * until it either succeeds or the server returns a non-WriteConflict error response. + * + * The caller should take care to ensure 'func' doesn't modify any captured variables in a + * speculative fashion where calling it multiple times would lead to unintended behavior. The + * transaction started by the withTxnAndAutoRetryOnWriteConflict() function is only known to + * have committed after the withTxnAndAutoRetryOnWriteConflict() function returns. + */ + function withTxnAndAutoRetryOnWriteConflict( + session, func, {txnOptions: txnOptions = {readConcern: {level: 'snapshot'}}} = {}) { + let hasWriteConflict; + + do { + session.startTransaction(txnOptions); + hasWriteConflict = false; + + try { + func(); + + // commitTransaction() calls assert.commandWorked(), which may fail with a + // WriteConflict error response. We therefore suppress its doassert() output. + quietly(() => session.commitTransaction()); + } catch (e) { + try { + // abortTransaction() calls assert.commandWorked(), which may fail with a + // WriteConflict error response. We therefore suppress its doassert() output. + quietly(() => session.abortTransaction()); + } catch (e) { + // We ignore the error from abortTransaction() because the transaction may have + // implicitly been aborted by the server already and will therefore return a + // NoSuchTransaction error response. We need to call abortTransaction() in order + // to update the mongo shell's state such that it agrees no transaction is + // currently in progress on this session. + } + + if (e.code !== ErrorCodes.WriteConflict) { + throw e; + } + + hasWriteConflict = true; + } + } while (hasWriteConflict); + } + + return {withTxnAndAutoRetryOnWriteConflict}; +})(); |