diff --git a/lib/topologies/replset.js b/lib/topologies/replset.js index a47c64787..35f28a921 100644 --- a/lib/topologies/replset.js +++ b/lib/topologies/replset.js @@ -1218,7 +1218,9 @@ function executeWriteOperation(args, options, callback) { } // Per SDAM, remove primary from replicaset - self.s.replicaSetState.remove(self.s.replicaSetState.primary, { force: true }); + if (self.s.replicaSetState.primary) { + self.s.replicaSetState.remove(self.s.replicaSetState.primary, { force: true }); + } return callback(err); }; diff --git a/test/tests/unit/replset/step_down_tests.js b/test/tests/unit/replset/step_down_tests.js index 1bb047737..7044338bc 100644 --- a/test/tests/unit/replset/step_down_tests.js +++ b/test/tests/unit/replset/step_down_tests.js @@ -5,7 +5,7 @@ const ReplSet = require('../../../../lib/topologies/replset'); const mock = require('mongodb-mock-server'); const ReplSetFixture = require('../common').ReplSetFixture; -describe('Step Down (ReplSet)', function() { +describe.only('Step Down (ReplSet)', function() { class MyFixture extends ReplSetFixture { constructor() { super(); @@ -81,10 +81,20 @@ describe('Step Down (ReplSet)', function() { } let test; - before(() => (test = new MyFixture())); + beforeEach(() => (test = new MyFixture())); afterEach(() => mock.cleanup()); beforeEach(() => test.setup()); + function makeReplicaSet() { + return new ReplSet([test.primaryServer.address(), test.firstSecondaryServer.address()], { + setName: 'rs', + connectionTimeout: 3000, + socketTimeout: 0, + haInterval: 100, + size: 1 + }); + } + it('Should only issue a "not master" error once', { metadata: { requires: { @@ -92,16 +102,7 @@ describe('Step Down (ReplSet)', function() { } }, test: function(done) { - const replSet = new ReplSet( - [test.primaryServer.address(), test.firstSecondaryServer.address()], - { - setName: 'rs', - connectionTimeout: 3000, - socketTimeout: 0, - haInterval: 100, - size: 1 - } - ); + const replSet = makeReplicaSet(); replSet.on('error', done); replSet.on('connect', () => { @@ -164,4 +165,52 @@ describe('Step Down (ReplSet)', function() { replSet.connect(); } }); + + it('Should only attempt to remove primary once', { + metadata: { + requires: { + topology: 'single' + } + }, + test: function(done) { + const replSet = makeReplicaSet(); + + replSet.on('error', done); + replSet.on('connect', () => { + const cleanupAndDone = e => { + replSet.destroy(); + done(e); + }; + + test.nextState(); + + let counter = 2; + + function handler(err, result) { + counter -= 1; + try { + expect(err).to.exist; + expect(err.message).to.match(/not master/); + expect(result).to.not.exist; + } catch (e) { + return cleanupAndDone(e); + } + + if (counter <= 0) { + cleanupAndDone(); + } + } + + // Should issue a "not master", since primary has stepped down + // This one will attempt to remove the primary + replSet.insert('foo.bar', [{ b: 2 }], handler); + + // Should issue a "not master", since primary has stepped down + // This one will not attempt to remove the primary, as it has + // already been removed + replSet.insert('foo.bar', [{ c: 3 }], handler); + }); + replSet.connect(); + } + }); });