summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml58
-rw-r--r--etc/evergreen.yml23
-rw-r--r--jstests/libs/override_methods/auto_retry_on_network_error.js5
-rw-r--r--jstests/libs/override_methods/retry_writes_at_least_once.js81
-rw-r--r--jstests/libs/retryable_writes_util.js16
-rw-r--r--src/mongo/shell/session.js8
-rw-r--r--src/mongo/shell/utils.js1
7 files changed, 189 insertions, 3 deletions
diff --git a/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml
index 6724df8d936..b91af9d9f45 100644
--- a/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml
@@ -10,14 +10,72 @@ selector:
- jstests/core/opcounters_write_cmd.js
- jstests/core/read_after_optime.js
+ # No-op retries are not ignored by top, the profiler, or opcount.
+ - jstests/core/operation_latency_histogram.js
+ - jstests/core/profile2.js
+ - jstests/core/profile3.js
+ - jstests/core/profile_findandmodify.js
+ - jstests/core/top.js
+ - jstests/core/views/views_stats.js
+
+ # TODO SERVER-31249: getLastError should not be affected by no-op retries.
+ - jstests/core/bulk_legacy_enforce_gle.js
+
+ # TODO SERVER-30532: Add lastErrorObject.upserted to findAndModify no-op retry response.
+ - jstests/core/find_and_modify_server6582.js
+
+ # TODO SERVER-31242: findAndModify no-op retry should respect the fields option.
+ - jstests/core/crud_api.js
+ - jstests/core/find_and_modify2.js
+ - jstests/core/find_and_modify_server6865.js
+
+ # TODO SERVER-31243: No-op retry of a findAndModify upsert of a new document should return null
+ # if new=false.
+ - jstests/core/find_and_modify.js
+ - jstests/core/find_and_modify4.js
+ - jstests/core/find_and_modify_empty_coll.js
+ - jstests/core/find_and_modify_empty_update.js
+ - jstests/core/find_and_modify_server6226.js
+
+ # TODO SERVER-31245: Inserts to "system.indexes" bypass the check for retryability.
+ - jstests/core/batch_write_command_insert.js
+
+ # TODO SERVER-31328: Investigate enabling these workloads once performance of retrying completed
+ # inserts and deletes has improved.
+ #
+ # These tests time out because of too many retryable operations:
+ - jstests/core/bulk_insert.js
+ - jstests/core/insert1.js
+ - jstests/core/remove6.js
+ - jstests/core/removea.js
+
+ # TODO SERVER-29843 / SERVER-31328: Investigate enabling these workloads once performance of
+ # retrying completed writes has improved.
+ #
+ # These tests time out on slower machines because of too many retryable operations:
+ - jstests/core/explain3.js
+ - jstests/core/geo2.js
+ - jstests/core/geo3.js
+ - jstests/core/geo_polygon3.js
+ - jstests/core/geo_s2explain.js
+ - jstests/core/geo_s2sparse.js
+ - jstests/core/geo_s2twofields.js
+ - jstests/core/mr1.js
+ - jstests/core/queryoptimizer3.js
+ - jstests/core/remove9.js
+ - jstests/core/removeb.js
+ - jstests/core/splitvector.js
+
executor:
config:
shell_options:
eval: >-
testingReplication = true;
load("jstests/libs/override_methods/enable_sessions.js");
+ load("jstests/libs/override_methods/retry_writes_at_least_once.js");
global_vars:
TestData:
+ alwaysInjectTransactionNumber: true
sessionOptions:
retryWrites: true
readMode: commands
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index 05331d63a3c..c3e71e8542d 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -3989,6 +3989,17 @@ tasks:
run_multiple_jobs: true
- <<: *task_template
+ name: retryable_writes_jscore_passthrough
+ depends_on:
+ - name: jsCore
+ commands:
+ - func: "do setup"
+ - func: "run tests"
+ vars:
+ resmoke_args: --suites=retryable_writes_jscore_passthrough --storageEngine=mmapv1
+ run_multiple_jobs: true
+
+- <<: *task_template
name: retryable_writes_jscore_passthrough_WT
depends_on:
- name: jsCore_WT
@@ -4890,6 +4901,7 @@ buildvariants:
- name: replica_sets_resync_static_jscore_passthrough
- name: replica_sets_resync_static_jscore_passthrough_WT
- name: replica_sets_pv0
+ - name: retryable_writes_jscore_passthrough
- name: retryable_writes_jscore_passthrough_WT
- name: master_slave
- name: master_slave_WT
@@ -6167,6 +6179,9 @@ buildvariants:
- name: replica_sets_kill_secondaries_jscore_passthrough_WT
distros:
- windows-64-vs2015-large
+ - name: retryable_writes_jscore_passthrough
+ distros:
+ - windows-64-vs2015-large
- name: retryable_writes_jscore_passthrough_WT
distros:
- windows-64-vs2015-large
@@ -6805,6 +6820,7 @@ buildvariants:
- name: replica_sets_jscore_passthrough
- name: replica_sets_jscore_passthrough_WT
- name: replica_sets_kill_secondaries_jscore_passthrough_WT
+ - name: retryable_writes_jscore_passthrough
- name: retryable_writes_jscore_passthrough_WT
- name: serial_run
- name: serial_run_WT
@@ -7195,6 +7211,9 @@ buildvariants:
- name: replica_sets_kill_secondaries_jscore_passthrough_WT
distros:
- rhel62-large
+ - name: retryable_writes_jscore_passthrough
+ distros:
+ - rhel62-large
- name: retryable_writes_jscore_passthrough_WT
distros:
- rhel62-large
@@ -7381,6 +7400,7 @@ buildvariants:
- name: replica_sets_resync_static_jscore_passthrough
- name: replica_sets_resync_static_jscore_passthrough_WT
- name: replica_sets_kill_secondaries_jscore_passthrough_WT
+ - name: retryable_writes_jscore_passthrough
- name: retryable_writes_jscore_passthrough_WT
- name: sasl
- name: session_jscore_passthrough_WT
@@ -9449,6 +9469,7 @@ buildvariants:
- name: replica_sets_auth
- name: replica_sets_pv0
- name: replica_sets_jscore_passthrough
+ - name: retryable_writes_jscore_passthrough
- name: retryable_writes_jscore_passthrough_WT
- name: rlp
- name: sasl
@@ -9930,6 +9951,7 @@ buildvariants:
- name: replica_sets_resync_static_jscore_passthrough
- name: replica_sets_resync_static_jscore_passthrough_WT
- name: replica_sets_kill_secondaries_jscore_passthrough_WT
+ - name: retryable_writes_jscore_passthrough
- name: retryable_writes_jscore_passthrough_WT
- name: sasl
- name: session_jscore_passthrough_WT
@@ -10137,6 +10159,7 @@ buildvariants:
- name: replica_sets_resync_static_jscore_passthrough
- name: replica_sets_resync_static_jscore_passthrough_WT
- name: replica_sets_kill_secondaries_jscore_passthrough_WT
+ - name: retryable_writes_jscore_passthrough
- name: retryable_writes_jscore_passthrough_WT
- name: sasl
- name: session_jscore_passthrough_WT
diff --git a/jstests/libs/override_methods/auto_retry_on_network_error.js b/jstests/libs/override_methods/auto_retry_on_network_error.js
index 9509eb372da..3386c468428 100644
--- a/jstests/libs/override_methods/auto_retry_on_network_error.js
+++ b/jstests/libs/override_methods/auto_retry_on_network_error.js
@@ -11,8 +11,7 @@
(function() {
"use strict";
- const retryableWriteCommands =
- new Set(["delete", "findandmodify", "findAndModify", "insert", "update"]);
+ load("jstests/libs/retryable_writes_util.js");
// Store a session to access ServerSession#canRetryWrites.
let _serverSession;
@@ -48,7 +47,7 @@
cmdName = Object.keys(cmdObj)[0];
}
- const isRetryableWriteCmd = retryableWriteCommands.has(cmdName);
+ const isRetryableWriteCmd = RetryableWritesUtil.isRetryableWriteCmdName(cmdName);
const canRetryWrites = _serverSession.canRetryWrites(cmdObj);
let numRetries = !jsTest.options().skipRetryOnNetworkError ? 1 : 0;
diff --git a/jstests/libs/override_methods/retry_writes_at_least_once.js b/jstests/libs/override_methods/retry_writes_at_least_once.js
new file mode 100644
index 00000000000..f0562c958d1
--- /dev/null
+++ b/jstests/libs/override_methods/retry_writes_at_least_once.js
@@ -0,0 +1,81 @@
+/**
+ * Overrides Mongo.prototype.runCommand and Mongo.prototype.runCommandWithMetadata to retry all
+ * retryable writes at least once, randomly more than that, regardless of the outcome of the
+ * command. Returns the result of the latest attempt.
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/retryable_writes_util.js");
+
+ Random.setRandomSeed();
+
+ const kExtraRetryProbability = 0.2;
+
+ // Store a session to access ServerSession#canRetryWrites.
+ let _serverSession;
+
+ const mongoRunCommandOriginal = Mongo.prototype.runCommand;
+ const mongoRunCommandWithMetadataOriginal = Mongo.prototype.runCommandWithMetadata;
+
+ Mongo.prototype.runCommand = function runCommand(dbName, cmdObj, options) {
+ if (typeof _serverSession === "undefined") {
+ _serverSession = this.startSession()._serverSession;
+ }
+
+ return runWithRetries(this, cmdObj, mongoRunCommandOriginal, arguments);
+ };
+
+ Mongo.prototype.runCommandWithMetadata = function runCommandWithMetadata(
+ dbName, metadata, cmdObj) {
+ if (typeof _serverSession === "undefined") {
+ _serverSession = this.startSession()._serverSession;
+ }
+
+ return runWithRetries(this, cmdObj, mongoRunCommandWithMetadataOriginal, arguments);
+ };
+
+ function runWithRetries(mongo, cmdObj, clientFunction, clientFunctionArguments) {
+ let cmdName = Object.keys(cmdObj)[0];
+
+ // If the command is in a wrapped form, then we look for the actual command object
+ // inside the query/$query object.
+ if (cmdName === "query" || cmdName === "$query") {
+ cmdObj = cmdObj[cmdName];
+ cmdName = Object.keys(cmdObj)[0];
+ }
+
+ const isRetryableWriteCmd = RetryableWritesUtil.isRetryableWriteCmdName(cmdName);
+ const canRetryWrites = _serverSession.canRetryWrites(cmdObj);
+
+ let res = clientFunction.apply(mongo, clientFunctionArguments);
+
+ if (isRetryableWriteCmd && canRetryWrites) {
+ let retryAttempt = 1;
+ do {
+ print("*** Retry attempt: " + retryAttempt + ", for command: " + cmdName +
+ " with txnNumber: " + tojson(cmdObj.txnNumber) + ", and lsid: " +
+ tojson(cmdObj.lsid));
+ ++retryAttempt;
+ res = clientFunction.apply(mongo, clientFunctionArguments);
+ } while (Random.rand() <= kExtraRetryProbability);
+ }
+
+ return res;
+ }
+
+ const startParallelShellOriginal = startParallelShell;
+
+ startParallelShell = function(jsCode, port, noConnect) {
+ let newCode;
+ const overridesFile = "jstests/libs/override_methods/retry_writes_at_least_once.js";
+ if (typeof(jsCode) === "function") {
+ // Load the override file and immediately invoke the supplied function.
+ newCode = `load("${overridesFile}"); (${jsCode})();`;
+ } else {
+ newCode = `load("${overridesFile}"); ${jsCode};`;
+ }
+
+ return startParallelShellOriginal(newCode, port, noConnect);
+ };
+})();
diff --git a/jstests/libs/retryable_writes_util.js b/jstests/libs/retryable_writes_util.js
new file mode 100644
index 00000000000..ba3cd37afe0
--- /dev/null
+++ b/jstests/libs/retryable_writes_util.js
@@ -0,0 +1,16 @@
+/**
+ * Utilities for testing retryable writes.
+ */
+var RetryableWritesUtil = (function() {
+ const retryableWriteCommands =
+ new Set(["delete", "findandmodify", "findAndModify", "insert", "update"]);
+
+ /**
+ * Returns true if the command name is that of a retryable write command.
+ */
+ function isRetryableWriteCmdName(cmdName) {
+ return retryableWriteCommands.has(cmdName);
+ }
+
+ return {isRetryableWriteCmdName: isRetryableWriteCmdName};
+})();
diff --git a/src/mongo/shell/session.js b/src/mongo/shell/session.js
index 2df6468c729..9b55a26051c 100644
--- a/src/mongo/shell/session.js
+++ b/src/mongo/shell/session.js
@@ -70,6 +70,7 @@ var {
function SessionAwareClient(client) {
const kWireVersionSupportingLogicalSession = 6;
const kWireVersionSupportingCausalConsistency = 6;
+ const kWireVersionSupportingRetryableWrites = 6;
this.getReadPreference = function getReadPreference(driverSession) {
const sessionOptions = driverSession.getOptions();
@@ -180,6 +181,13 @@ var {
cmdObj = injectAfterClusterTime(cmdObj, driverSession._operationTime);
}
+ if (jsTest.options().alwaysInjectTransactionNumber &&
+ serverSupports(kWireVersionSupportingRetryableWrites) &&
+ driverSession.getOptions().shouldRetryWrites() &&
+ driverSession._serverSession.canRetryWrites(cmdObj)) {
+ cmdObj = driverSession._serverSession.assignTransactionNumber(cmdObj);
+ }
+
return cmdObj;
}
diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js
index 63b41528e49..7bdc79411fc 100644
--- a/src/mongo/shell/utils.js
+++ b/src/mongo/shell/utils.js
@@ -257,6 +257,7 @@ jsTestOptions = function() {
TestData.skipCheckingUUIDsConsistentAcrossCluster || false,
jsonSchemaTestFile: TestData.jsonSchemaTestFile,
excludedDBsFromDBHash: TestData.excludedDBsFromDBHash,
+ alwaysInjectTransactionNumber: TestData.alwaysInjectTransactionNumber,
});
}
return _jsTestOptions;