1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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);
};
})();
|