diff options
author | Vesselina Ratcheva <vesselina.ratcheva@10gen.com> | 2020-05-28 16:14:19 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-06-01 17:19:32 +0000 |
commit | f95ee0caa794a62eb37cf3537001853b4b78a0ec (patch) | |
tree | 47a3d7ea956b11ab1ebc1a029bffbbb9ccbee746 /src | |
parent | ae39837dc5bd5963172bd65ec0227c29cfd29042 (diff) | |
download | mongo-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.js | 79 |
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}); |