summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVesselina Ratcheva <vesselina.ratcheva@10gen.com>2020-05-28 16:14:19 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-06-01 17:19:32 +0000
commitf95ee0caa794a62eb37cf3537001853b4b78a0ec (patch)
tree47a3d7ea956b11ab1ebc1a029bffbbb9ccbee746 /src
parentae39837dc5bd5963172bd65ec0227c29cfd29042 (diff)
downloadmongo-f95ee0caa794a62eb37cf3537001853b4b78a0ec.tar.gz
SERVER-46346 Make rs.add() robust to config version too low errors
Diffstat (limited to 'src')
-rw-r--r--src/mongo/shell/utils.js79
1 files changed, 55 insertions, 24 deletions
diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js
index d3a2e32e55f..3a28eb1823c 100644
--- a/src/mongo/shell/utils.js
+++ b/src/mongo/shell/utils.js
@@ -1519,34 +1519,65 @@ rs.reconfig = function(cfg, options) {
return this._runCmd(cmd);
};
rs.add = function(hostport, arb) {
- var cfg = hostport;
+ let res;
+ let self = this;
- var local = db.getSisterDB("local");
- assert(local.system.replset.count() <= 1,
- "error: local.system.replset has unexpected contents");
- var c = local.system.replset.findOne();
- assert(c, "no config object retrievable from local.system.replset");
+ assert.soon(function() {
+ var cfg = hostport;
+
+ var local = db.getSisterDB("local");
+ assert(local.system.replset.count() <= 1,
+ "error: local.system.replset has unexpected contents");
+ var c = local.system.replset.findOne();
+ assert(c, "no config object retrievable from local.system.replset");
+
+ const attemptedVersion = c.version++;
+
+ var max = 0;
+ for (var i in c.members) {
+ // Omit 'newlyAdded' field if it exists in the config.
+ delete c.members[i].newlyAdded;
+ if (c.members[i]._id > max)
+ max = c.members[i]._id;
+ }
+ if (isString(hostport)) {
+ cfg = {_id: max + 1, host: hostport};
+ if (arb)
+ cfg.arbiterOnly = true;
+ } else if (arb == true) {
+ throw Error(
+ "Expected first parameter to be a host-and-port string of arbiter, but got " +
+ tojson(hostport));
+ }
- c.version++;
+ if (cfg._id == null) {
+ cfg._id = max + 1;
+ }
+ c.members.push(cfg);
- var max = 0;
- for (var i in c.members)
- if (c.members[i]._id > max)
- max = c.members[i]._id;
- if (isString(hostport)) {
- cfg = {_id: max + 1, host: hostport};
- if (arb)
- cfg.arbiterOnly = true;
- } else if (arb == true) {
- throw Error("Expected first parameter to be a host-and-port string of arbiter, but got " +
- tojson(hostport));
- }
+ res = self._runCmd({replSetReconfig: c});
+ if (res === "") {
+ // _runCmd caught an exception.
+ return true;
+ }
+ if (res.ok) {
+ return true;
+ }
+ if (res.code === ErrorCodes.ConfigurationInProgress) {
+ return false; // keep retrying
+ }
+ if (res.code === ErrorCodes.NewReplicaSetConfigurationIncompatible) {
+ // We will retry only if this error was due to our config version being too low.
+ const cfgState = local.system.replset.findOne();
+ if (cfgState.version >= attemptedVersion) {
+ return false; // keep retrying
+ }
+ }
+ // Take no action on other errors.
+ return true;
+ }, () => tojson(res), 10 * 60 * 1000 /* timeout */, 200 /* interval */);
- if (cfg._id == null) {
- cfg._id = max + 1;
- }
- c.members.push(cfg);
- return this._runCmd({replSetReconfig: c});
+ return res;
};
rs.syncFrom = function(host) {
return db._adminCommand({replSetSyncFrom: host});