From 0060ad7db18401891ed2724252dab26ae73b69f7 Mon Sep 17 00:00:00 2001 From: Dan Aprahamian Date: Tue, 19 Dec 2017 11:46:12 -0500 Subject: [PATCH] fix(secondaries): fixes connection with secondary readPreference (#258) Ensures that when readPreference is `secondary`, the `connect` event is not triggered until connection has been established with a secondary. Fixes NODE-1089 --- lib/topologies/replset.js | 21 ++++++-- .../unit/replset/read_preference_tests.js | 51 +++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 test/tests/unit/replset/read_preference_tests.js diff --git a/lib/topologies/replset.js b/lib/topologies/replset.js index 1a0f73877..a2932cb72 100644 --- a/lib/topologies/replset.js +++ b/lib/topologies/replset.js @@ -773,6 +773,22 @@ function applyAuthenticationContexts(self, server, callback) { applyAuth(authContexts, server, callback); } +function shouldTriggerConnect(self) { + const isConnecting = self.state === CONNECTING; + const hasPrimary = self.s.replicaSetState.hasPrimary(); + const hasSecondary = self.s.replicaSetState.hasSecondary(); + const secondaryOnlyConnectionAllowed = self.s.options.secondaryOnlyConnectionAllowed; + const readPreferenceSecondary = + self.s.connectOptions.readPreference && + self.s.connectOptions.readPreference.equals(ReadPreference.secondary); + + return ( + (isConnecting && + ((readPreferenceSecondary && hasSecondary) || (!readPreferenceSecondary && hasPrimary))) || + (hasSecondary && secondaryOnlyConnectionAllowed) + ); +} + function handleInitialConnectEvent(self, event) { return function() { var _this = this; @@ -835,10 +851,7 @@ function handleInitialConnectEvent(self, event) { _this.on('parseError', handleEvent(self, 'parseError')); // Do we have a primary or primaryAndSecondary - if ( - (self.state === CONNECTING && self.s.replicaSetState.hasPrimary()) || - (self.s.replicaSetState.hasSecondary() && self.s.options.secondaryOnlyConnectionAllowed) - ) { + if (shouldTriggerConnect(self)) { // We are connected self.state = CONNECTED; diff --git a/test/tests/unit/replset/read_preference_tests.js b/test/tests/unit/replset/read_preference_tests.js new file mode 100644 index 000000000..0a3c99bdd --- /dev/null +++ b/test/tests/unit/replset/read_preference_tests.js @@ -0,0 +1,51 @@ +'use strict'; + +const expect = require('chai').expect; +const ReplSet = require('../../../../lib/topologies/replset'); +const ReadPreference = require('../../../../lib/topologies/read_preference'); +const mock = require('../../../mock'); +const ReplSetFixture = require('../common').ReplSetFixture; + +describe('Secondaries (ReplSet)', function() { + let test; + before(() => (test = new ReplSetFixture())); + afterEach(() => mock.cleanup()); + beforeEach(() => test.setup()); + + it('Should not be "connected" with ReadPreference secondary unless secondary is connected', { + metadata: { + requires: { + topology: 'single' + } + }, + + test: function(done) { + const replSet = new ReplSet( + [test.primaryServer.address(), test.firstSecondaryServer.address()], + { + setName: 'rs', + connectionTimeout: 3000, + socketTimeout: 0, + haInterval: 100, + size: 1 + } + ); + + replSet.on('error', done); + + replSet.on('connect', server => { + let err; + try { + expect(server.s.replicaSetState.hasSecondary()).to.equal(true); + } catch (e) { + err = e; + } + + replSet.destroy(); + done(err); + }); + + replSet.connect({ readPreference: new ReadPreference('secondary') }); + } + }); +});