3 var inherits = require('util').inherits,
4 f = require('util').format,
5 EventEmitter = require('events').EventEmitter,
6 Logger = require('../connection/logger'),
7 ObjectId = require('bson').ObjectId,
8 ReadPreference = require('./read_preference'),
9 MongoError = require('../error');
12 'Single': 'Single', 'ReplicaSetNoPrimary': 'ReplicaSetNoPrimary',
13 'ReplicaSetWithPrimary': 'ReplicaSetWithPrimary', 'Sharded': 'Sharded',
18 'Standalone': 'Standalone', 'Mongos': 'Mongos', 'PossiblePrimary': 'PossiblePrimary',
19 'RSPrimary': 'RSPrimary', 'RSSecondary': 'RSSecondary', 'RSArbiter': 'RSArbiter',
20 'RSOther': 'RSOther', 'RSGhost': 'RSGhost', 'Unknown': 'Unknown'
23 var ReplSetState = function(options) {
24 options = options || {};
26 EventEmitter.call(this);
28 this.topologyType = TopologyType.ReplicaSetNoPrimary;
29 this.setName = options.setName;
36 this.setName = options.setName;
39 this.logger = options.logger || Logger('ReplSet', options);
41 // Server selection index
44 this.acceptableLatency = options.acceptableLatency || 15;
46 // heartbeatFrequencyMS
47 this.heartbeatFrequencyMS = options.heartbeatFrequencyMS || 10000;
51 this.secondaries = [];
55 // Current unknown hosts
56 this.unknownServers = [];
60 this.maxElectionId = null;
61 this.maxSetVersion = 0;
62 // Description of the Replicaset
63 this.replicasetDescription = {
64 "topologyType": "Unknown", "servers": []
68 inherits(ReplSetState, EventEmitter);
70 ReplSetState.prototype.hasPrimaryAndSecondary = function(server) {
71 return this.primary != null && this.secondaries.length > 0;
74 ReplSetState.prototype.hasPrimary = function(server) {
75 return this.primary != null;
78 ReplSetState.prototype.hasSecondary = function(server) {
79 return this.secondaries.length > 0;
82 ReplSetState.prototype.allServers = function(options) {
83 options = options || {};
84 var servers = this.primary ? [this.primary] : [];
85 servers = servers.concat(this.secondaries);
86 if(!options.ignoreArbiters) servers = servers.concat(this.arbiters);
87 servers = servers.concat(this.passives);
91 ReplSetState.prototype.destroy = function() {
92 // Destroy all sockets
93 if(this.primary) this.primary.destroy();
94 this.secondaries.forEach(function(x) { x.destroy(); });
95 this.arbiters.forEach(function(x) { x.destroy(); });
96 this.passives.forEach(function(x) { x.destroy(); });
97 this.ghosts.forEach(function(x) { x.destroy(); });
98 // Clear out the complete state
99 this.secondaries = [];
103 this.unknownServers = [];
107 ReplSetState.prototype.remove = function(server, options) {
108 options = options || {};
110 // Only remove if the current server is not connected
111 var servers = this.primary ? [this.primary] : [];
112 servers = servers.concat(this.secondaries);
113 servers = servers.concat(this.arbiters);
114 servers = servers.concat(this.passives);
116 // Check if it's active and this is just a failed connection attempt
117 for(var i = 0; i < servers.length; i++) {
118 if(!options.force && servers[i].equals(server) && servers[i].isConnected && servers[i].isConnected()) {
123 // If we have it in the set remove it
124 if(this.set[server.name]) {
125 this.set[server.name].type = ServerType.Unknown;
126 this.set[server.name].electionId = null;
127 this.set[server.name].setName = null;
128 this.set[server.name].setVersion = null;
132 var removeType = null;
134 // Remove from any lists
135 if(this.primary && this.primary.equals(server)) {
137 this.topologyType = TopologyType.ReplicaSetNoPrimary;
138 removeType = 'primary';
141 // Remove from any other server lists
142 removeType = removeFrom(server, this.secondaries) ? 'secondary' : removeType;
143 removeType = removeFrom(server, this.arbiters) ? 'arbiter' : removeType;
144 removeType = removeFrom(server, this.passives) ? 'secondary' : removeType;
145 removeFrom(server, this.ghosts);
146 removeFrom(server, this.unknownServers);
148 // Do we have a removeType
150 this.emit('left', removeType, server);
154 ReplSetState.prototype.update = function(server) {
156 // Get the current ismaster
157 var ismaster = server.lastIsMaster();
163 // Join all the possible new hosts
164 var hosts = Array.isArray(ismaster.hosts) ? ismaster.hosts : [];
165 hosts = hosts.concat(Array.isArray(ismaster.arbiters) ? ismaster.arbiters : []);
166 hosts = hosts.concat(Array.isArray(ismaster.passives) ? ismaster.passives : []);
168 // Add all hosts as unknownServers
169 for(var i = 0; i < hosts.length; i++) {
170 // Add to the list of unknown server
171 if(this.unknownServers.indexOf(hosts[i]) == -1
172 && (!this.set[hosts[i]] || this.set[hosts[i]].type == ServerType.Unknown)) {
173 this.unknownServers.push(hosts[i]);
176 if(!this.set[hosts[i]]) {
177 this.set[hosts[i]] = {
178 type: ServerType.Unknown,
190 if(!ismaster && !inList(ismaster, server, this.unknownServers)) {
191 self.set[server.name] = {
192 type: ServerType.Unknown, setVersion: null, electionId: null, setName: null
194 // Update set information about the server instance
195 self.set[server.name].type = ServerType.Unknown;
196 self.set[server.name].electionId = ismaster ? ismaster.electionId : ismaster;
197 self.set[server.name].setName = ismaster ? ismaster.setName : ismaster;
198 self.set[server.name].setVersion = ismaster ? ismaster.setVersion : ismaster;
200 if(self.unknownServers.indexOf(server.name) == -1) {
201 self.unknownServers.push(server.name);
211 if(ismaster && ismaster.msg == 'isdbgrid') {
215 // A RSOther instance
216 if((ismaster.setName && ismaster.hidden)
217 || (ismaster.setName && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly && !ismaster.passive)) {
218 self.set[server.name] = {
219 type: ServerType.RSOther, setVersion: null,
220 electionId: null, setName: ismaster.setName
223 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
224 if(ismaster.setName) this.setName = ismaster.setName;
228 // A RSGhost instance
229 if(ismaster.isreplicaset) {
230 self.set[server.name] = {
231 type: ServerType.RSGhost, setVersion: null,
232 electionId: null, setName: null
236 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
237 if(ismaster.setName) this.setName = ismaster.setName;
244 // Standalone server, destroy and return
246 if(ismaster && ismaster.ismaster && !ismaster.setName) {
247 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
248 this.remove(server, {force:true});
253 // Server in maintanance mode
255 if(ismaster && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly) {
256 this.remove(server, {force:true});
261 // If the .me field does not match the passed in server
263 if(ismaster.me && ismaster.me != server.name) {
264 if(this.logger.isWarn()) {
265 this.logger.warn(f('the seedlist server was removed due to its address %s not matching its ismaster.me address %s', server.name, ismaster.me));
268 // Set the type of topology we have
269 if(this.primary && !this.primary.equals(server)) {
270 this.topologyType = TopologyType.ReplicaSetWithPrimary;
272 this.topologyType = TopologyType.ReplicaSetNoPrimary;
281 if(!this.primary && ismaster.ismaster && ismaster.setName) {
282 var ismasterElectionId = server.lastIsMaster().electionId;
283 if(this.setName && this.setName != ismaster.setName) {
284 this.topologyType = TopologyType.ReplicaSetNoPrimary;
285 return new MongoError(f('setName from ismaster does not match provided connection setName [%s] != [%s]', ismaster.setName, this.setName));
288 if(!this.maxElectionId && ismasterElectionId) {
289 this.maxElectionId = ismasterElectionId;
290 } else if(this.maxElectionId && ismasterElectionId) {
291 var result = compareObjectIds(this.maxElectionId, ismasterElectionId);
292 // Get the electionIds
293 var ismasterSetVersion = server.lastIsMaster().setVersion;
295 // if(result == 1 || result == 0) {
297 this.topologyType = TopologyType.ReplicaSetNoPrimary;
299 } else if(result == 0 && ismasterSetVersion) {
300 if(ismasterSetVersion < this.maxSetVersion) {
301 this.topologyType = TopologyType.ReplicaSetNoPrimary;
306 this.maxSetVersion = ismasterSetVersion;
307 this.maxElectionId = ismasterElectionId;
310 self.primary = server;
311 self.set[server.name] = {
312 type: ServerType.RSPrimary,
313 setVersion: ismaster.setVersion,
314 electionId: ismaster.electionId,
315 setName: ismaster.setName
319 this.topologyType = TopologyType.ReplicaSetWithPrimary;
320 if(ismaster.setName) this.setName = ismaster.setName;
321 removeFrom(server, self.unknownServers);
322 removeFrom(server, self.secondaries);
323 removeFrom(server, self.passives);
324 self.emit('joined', 'primary', server);
325 emitTopologyDescriptionChanged(self);
327 } else if(ismaster.ismaster && ismaster.setName) {
328 // Get the electionIds
329 var currentElectionId = self.set[self.primary.name].electionId;
330 var currentSetVersion = self.set[self.primary.name].setVersion;
331 var currentSetName = self.set[self.primary.name].setName;
332 var ismasterElectionId = server.lastIsMaster().electionId;
333 var ismasterSetVersion = server.lastIsMaster().setVersion;
334 var ismasterSetName = server.lastIsMaster().setName;
336 // Is it the same server instance
337 if(this.primary.equals(server)
338 && currentSetName == ismasterSetName) {
342 // If we do not have the same rs name
343 if(currentSetName && currentSetName != ismasterSetName) {
344 if(!this.primary.equals(server)) {
345 this.topologyType = TopologyType.ReplicaSetWithPrimary;
347 this.topologyType = TopologyType.ReplicaSetNoPrimary;
353 // Check if we need to replace the server
354 if(currentElectionId && ismasterElectionId) {
355 var result = compareObjectIds(currentElectionId, ismasterElectionId);
359 } else if(result == 0 && (currentSetVersion > ismasterSetVersion)) {
362 } else if(!currentElectionId && ismasterElectionId
363 && ismasterSetVersion) {
364 if(ismasterSetVersion < this.maxSetVersion) {
369 if(!this.maxElectionId && ismasterElectionId) {
370 this.maxElectionId = ismasterElectionId;
371 } else if(this.maxElectionId && ismasterElectionId) {
372 var result = compareObjectIds(this.maxElectionId, ismasterElectionId);
376 } else if(result == 0 && currentSetVersion && ismasterSetVersion) {
377 if(ismasterSetVersion < this.maxSetVersion) {
381 if(ismasterSetVersion < this.maxSetVersion) {
386 this.maxElectionId = ismasterElectionId;
387 this.maxSetVersion = ismasterSetVersion;
389 this.maxSetVersion = ismasterSetVersion;
392 // Modify the entry to unknown
393 self.set[self.primary.name] = {
394 type: ServerType.Unknown, setVersion: null,
395 electionId: null, setName: null
398 // Signal primary left
399 self.emit('left', 'primary', this.primary);
400 // Destroy the instance
401 self.primary.destroy();
402 // Set the new instance
403 self.primary = server;
404 // Set the set information
405 self.set[server.name] = {
406 type: ServerType.RSPrimary, setVersion: ismaster.setVersion,
407 electionId: ismaster.electionId, setName: ismaster.setName
411 this.topologyType = TopologyType.ReplicaSetWithPrimary;
412 if(ismaster.setName) this.setName = ismaster.setName;
413 removeFrom(server, self.unknownServers);
414 removeFrom(server, self.secondaries);
415 removeFrom(server, self.passives);
416 self.emit('joined', 'primary', server);
417 emitTopologyDescriptionChanged(self);
421 // A possible instance
422 if(!this.primary && ismaster.primary) {
423 self.set[ismaster.primary] = {
424 type: ServerType.PossiblePrimary, setVersion: null,
425 electionId: null, setName: null
430 // Secondary handling
432 if(ismaster.secondary && ismaster.setName
433 && !inList(ismaster, server, this.secondaries)
434 && this.setName && this.setName == ismaster.setName) {
435 addToList(self, ServerType.RSSecondary, ismaster, server, this.secondaries);
437 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
438 if(ismaster.setName) this.setName = ismaster.setName;
439 removeFrom(server, self.unknownServers);
442 if(this.primary && this.primary.name == server.name) {
445 self.emit('left', 'primary', server);
448 self.emit('joined', 'secondary', server);
449 emitTopologyDescriptionChanged(self);
456 if(ismaster.arbiterOnly && ismaster.setName
457 && !inList(ismaster, server, this.arbiters)
458 && this.setName && this.setName == ismaster.setName) {
459 addToList(self, ServerType.RSArbiter, ismaster, server, this.arbiters);
461 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
462 if(ismaster.setName) this.setName = ismaster.setName;
463 removeFrom(server, self.unknownServers);
464 self.emit('joined', 'arbiter', server);
465 emitTopologyDescriptionChanged(self);
472 if(ismaster.passive && ismaster.setName
473 && !inList(ismaster, server, this.passives)
474 && this.setName && this.setName == ismaster.setName) {
475 addToList(self, ServerType.RSSecondary, ismaster, server, this.passives);
477 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
478 if(ismaster.setName) this.setName = ismaster.setName;
479 removeFrom(server, self.unknownServers);
482 if(this.primary && this.primary.name == server.name) {
485 self.emit('left', 'primary', server);
488 self.emit('joined', 'secondary', server);
489 emitTopologyDescriptionChanged(self);
494 // Remove the primary
496 if(this.set[server.name] && this.set[server.name].type == ServerType.RSPrimary) {
497 self.emit('left', 'primary', this.primary);
498 this.primary.destroy();
500 this.topologyType = TopologyType.ReplicaSetNoPrimary;
504 this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.ReplicaSetNoPrimary;
509 * Recalculate single server max staleness
512 ReplSetState.prototype.updateServerMaxStaleness = function(server, haInterval) {
513 // Locate the max secondary lastwrite
515 // Go over all secondaries
516 for(var i = 0; i < this.secondaries.length; i++) {
517 max = Math.max(max, this.secondaries[i].lastWriteDate);
520 // Perform this servers staleness calculation
521 if(server.ismaster.maxWireVersion >= 5
522 && server.ismaster.secondary
523 && this.hasPrimary()) {
524 server.staleness = (server.lastUpdateTime - server.lastWriteDate)
525 - (this.primary.lastUpdateTime - this.primary.lastWriteDate)
527 } else if(server.ismaster.maxWireVersion >= 5
528 && server.ismaster.secondary){
529 server.staleness = max - server.lastWriteDate + haInterval;
534 * Recalculate all the stalness values for secodaries
537 ReplSetState.prototype.updateSecondariesMaxStaleness = function(haInterval) {
538 for(var i = 0; i < this.secondaries.length; i++) {
539 this.updateServerMaxStaleness(this.secondaries[i], haInterval);
544 * Pick a server by the passed in ReadPreference
546 * @param {ReadPreference} readPreference The ReadPreference instance to use
548 ReplSetState.prototype.pickServer = function(readPreference) {
549 // If no read Preference set to primary by default
550 readPreference = readPreference || ReadPreference.primary;
552 // maxStalenessMS is not allowed with a primary read
553 if(readPreference.preference == 'primary' && readPreference.maxStalenessMS) {
554 return new MongoError('primary readPreference incompatible with maxStalenessMS');
557 // Check if we have any non compatible servers for maxStalenessMS
558 var allservers = this.primary ? [this.primary] : [];
559 allservers = allservers.concat(this.secondaries);
561 // Does any of the servers not support the right wire protocol version
562 // for maxStalenessMS when maxStalenessMS specified on readPreference. Then error out
563 if(readPreference.maxStalenessMS) {
564 for(var i = 0; i < allservers.length; i++) {
565 if(allservers[i].ismaster.maxWireVersion < 5) {
566 return new MongoError('maxStalenessMS not supported by at least one of the replicaset members');
571 // Do we have the nearest readPreference
572 if(readPreference.preference == 'nearest' && !readPreference.maxStalenessMS) {
573 return pickNearest(this, readPreference);
574 } else if(readPreference.preference == 'nearest' && readPreference.maxStalenessMS) {
575 return pickNearestMaxStalenessMS(this, readPreference);
578 // Get all the secondaries
579 var secondaries = this.secondaries;
581 // Check if we can satisfy and of the basic read Preferences
582 if(readPreference.equals(ReadPreference.secondary)
583 && secondaries.length == 0) {
584 return new MongoError("no secondary server available");
587 if(readPreference.equals(ReadPreference.secondaryPreferred)
588 && secondaries.length == 0
589 && this.primary == null) {
590 return new MongoError("no secondary or primary server available");
593 if(readPreference.equals(ReadPreference.primary)
594 && this.primary == null) {
595 return new MongoError("no primary server available");
598 // Secondary preferred or just secondaries
599 if(readPreference.equals(ReadPreference.secondaryPreferred)
600 || readPreference.equals(ReadPreference.secondary)) {
602 if(secondaries.length > 0 && !readPreference.maxStalenessMS) {
603 // Pick nearest of any other servers available
604 var server = pickNearest(this, readPreference);
605 // No server in the window return primary
609 } else if(secondaries.length > 0 && readPreference.maxStalenessMS) {
610 // Pick nearest of any other servers available
611 var server = pickNearestMaxStalenessMS(this, readPreference);
612 // No server in the window return primary
618 if(readPreference.equals(ReadPreference.secondaryPreferred)){
626 if(readPreference.equals(ReadPreference.primaryPreferred)) {
629 // We prefer the primary if it's available
635 if(secondaries.length > 0 && !readPreference.maxStalenessMS) {
636 server = pickNearest(this, readPreference);
637 } else if(secondaries.length > 0 && readPreference.maxStalenessMS) {
638 server = pickNearestMaxStalenessMS(this, readPreference);
641 // Did we find a server
642 if(server) return server;
645 // Return the primary
650 // Filter serves by tags
651 var filterByTags = function(readPreference, servers) {
652 if(readPreference.tags == null) return servers;
653 var filteredServers = [];
654 var tagsArray = Array.isArray(readPreference.tags) ? readPreference.tags : [readPreference.tags];
656 // Iterate over the tags
657 for(var j = 0; j < tagsArray.length; j++) {
658 var tags = tagsArray[j];
660 // Iterate over all the servers
661 for(var i = 0; i < servers.length; i++) {
662 var serverTag = servers[i].lastIsMaster().tags || {};
664 // Did we find the a matching server
666 // Check if the server is valid
667 for(var name in tags) {
668 if(serverTag[name] != tags[name]) found = false;
671 // Add to candidate list
673 filteredServers.push(servers[i]);
677 // We found servers by the highest priority
681 // Returned filtered servers
682 return filteredServers;
685 function pickNearestMaxStalenessMS(self, readPreference) {
686 // Only get primary and secondaries as seeds
689 var heartbeatFrequencyMS = self.heartbeatFrequencyMS;
691 // Check if the maxStalenessMS > heartbeatFrequencyMS * 2
692 if(readPreference.maxStalenessMS < (heartbeatFrequencyMS * 2)) {
693 return new MongoError('maxStalenessMS must be at least twice the haInterval');
696 // Add primary to list if not a secondary read preference
697 if(self.primary && readPreference.preference != 'secondary') {
698 servers.push(self.primary);
701 // Add all the secondaries
702 for(var i = 0; i < self.secondaries.length; i++) {
703 servers.push(self.secondaries[i]);
707 servers = filterByTags(readPreference, servers);
710 // Locate lowest time (picked servers are lowest time + acceptable Latency margin)
711 // var lowest = servers.length > 0 ? servers[0].lastIsMasterMS : 0;
714 servers = servers.filter(function(s) {
715 return s.staleness <= readPreference.maxStalenessMS;
719 servers.sort(function(a, b) {
720 // return a.time > b.time;
721 return a.lastIsMasterMS > b.lastIsMasterMS
724 // No servers, default to primary
725 if(servers.length == 0) {
729 // Ensure index does not overflow the number of available servers
730 self.index = self.index % servers.length;
733 var server = servers[self.index];
735 self.index = self.index + 1;
736 // Return the first server of the sorted and filtered list
740 function pickNearest(self, readPreference) {
741 // Only get primary and secondaries as seeds
745 // Add primary to list if not a secondary read preference
746 if(self.primary && readPreference.preference != 'secondary') {
747 servers.push(self.primary);
750 // Add all the secondaries
751 for(var i = 0; i < self.secondaries.length; i++) {
752 servers.push(self.secondaries[i]);
756 servers = filterByTags(readPreference, servers);
759 servers.sort(function(a, b) {
760 // return a.time > b.time;
761 return a.lastIsMasterMS > b.lastIsMasterMS
764 // Locate lowest time (picked servers are lowest time + acceptable Latency margin)
765 var lowest = servers.length > 0 ? servers[0].lastIsMasterMS : 0;
768 servers = servers.filter(function(s) {
769 return s.lastIsMasterMS <= lowest + self.acceptableLatency;
772 // No servers, default to primary
773 if(servers.length == 0) {
777 // Ensure index does not overflow the number of available servers
778 self.index = self.index % servers.length;
780 var server = servers[self.index];
782 self.index = self.index + 1;
783 // Return the first server of the sorted and filtered list
787 function inList(ismaster, server, list) {
788 for(var i = 0; i < list.length; i++) {
789 if(list[i].name == server.name) return true;
795 function addToList(self, type, ismaster, server, list) {
796 // Update set information about the server instance
797 self.set[server.name].type = type;
798 self.set[server.name].electionId = ismaster ? ismaster.electionId : ismaster;
799 self.set[server.name].setName = ismaster ? ismaster.setName : ismaster;
800 self.set[server.name].setVersion = ismaster ? ismaster.setVersion : ismaster;
805 function compareObjectIds(id1, id2) {
806 var a = new Buffer(id1.toHexString(), 'hex');
807 var b = new Buffer(id2.toHexString(), 'hex');
813 if(typeof Buffer.compare === 'function') {
814 return Buffer.compare(a, b);
819 var len = Math.min(x, y);
821 for (var i = 0; i < len; i++) {
832 return x < y ? -1 : y < x ? 1 : 0;
835 function removeFrom(server, list) {
836 for(var i = 0; i < list.length; i++) {
837 if(list[i].equals && list[i].equals(server)) {
840 } else if(typeof list[i] == 'string' && list[i] == server.name) {
849 function emitTopologyDescriptionChanged(self) {
850 if(self.listeners('topologyDescriptionChanged').length > 0) {
851 var topology = 'Unknown';
852 var setName = self.setName;
854 if(self.hasPrimaryAndSecondary()) {
855 topology = 'ReplicaSetWithPrimary';
856 } else if(!self.hasPrimary() && self.hasSecondary()) {
857 topology = 'ReplicaSetNoPrimary';
860 // Generate description
862 topologyType: topology,
867 // Add the primary to the list
868 if(self.hasPrimary()) {
869 var desc = self.primary.getDescription();
870 desc.type = 'RSPrimary';
871 description.servers.push(desc);
874 // Add all the secondaries
875 description.servers = description.servers.concat(self.secondaries.map(function(x) {
876 var description = x.getDescription();
877 description.type = 'RSSecondary';
881 // Add all the arbiters
882 description.servers = description.servers.concat(self.arbiters.map(function(x) {
883 var description = x.getDescription();
884 description.type = 'RSArbiter';
888 // Add all the passives
889 description.servers = description.servers.concat(self.passives.map(function(x) {
890 var description = x.getDescription();
891 description.type = 'RSSecondary';
898 previousDescription: self.replicasetDescription,
899 newDescription: description,
900 diff: diff(self.replicasetDescription, description)
903 // Emit the topologyDescription change
904 self.emit('topologyDescriptionChanged', result);
906 // Set the new description
907 self.replicasetDescription = description;
911 function diff(previous, current) {
912 // Difference document
919 previous = { servers: [] };
922 // Got through all the servers
923 for(var i = 0; i < previous.servers.length; i++) {
924 var prevServer = previous.servers[i];
926 // Go through all current servers
927 for(var j = 0; j < current.servers.length; j++) {
928 var currServer = current.servers[j];
931 if(prevServer.address === currServer.address) {
932 // We had a change in state
933 if(prevServer.type != currServer.type) {
935 address: prevServer.address,
936 from: prevServer.type,
948 module.exports = ReplSetState;