replset_state.js 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  1. 'use strict';
  2. var inherits = require('util').inherits,
  3. f = require('util').format,
  4. diff = require('./shared').diff,
  5. EventEmitter = require('events').EventEmitter,
  6. Logger = require('../connection/logger'),
  7. ReadPreference = require('./read_preference'),
  8. MongoError = require('../error').MongoError,
  9. Buffer = require('safe-buffer').Buffer;
  10. var TopologyType = {
  11. Single: 'Single',
  12. ReplicaSetNoPrimary: 'ReplicaSetNoPrimary',
  13. ReplicaSetWithPrimary: 'ReplicaSetWithPrimary',
  14. Sharded: 'Sharded',
  15. Unknown: 'Unknown'
  16. };
  17. var ServerType = {
  18. Standalone: 'Standalone',
  19. Mongos: 'Mongos',
  20. PossiblePrimary: 'PossiblePrimary',
  21. RSPrimary: 'RSPrimary',
  22. RSSecondary: 'RSSecondary',
  23. RSArbiter: 'RSArbiter',
  24. RSOther: 'RSOther',
  25. RSGhost: 'RSGhost',
  26. Unknown: 'Unknown'
  27. };
  28. var ReplSetState = function(options) {
  29. options = options || {};
  30. // Add event listener
  31. EventEmitter.call(this);
  32. // Topology state
  33. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  34. this.setName = options.setName;
  35. // Server set
  36. this.set = {};
  37. // Unpacked options
  38. this.id = options.id;
  39. this.setName = options.setName;
  40. // Replicaset logger
  41. this.logger = options.logger || Logger('ReplSet', options);
  42. // Server selection index
  43. this.index = 0;
  44. // Acceptable latency
  45. this.acceptableLatency = options.acceptableLatency || 15;
  46. // heartbeatFrequencyMS
  47. this.heartbeatFrequencyMS = options.heartbeatFrequencyMS || 10000;
  48. // Server side
  49. this.primary = null;
  50. this.secondaries = [];
  51. this.arbiters = [];
  52. this.passives = [];
  53. this.ghosts = [];
  54. // Current unknown hosts
  55. this.unknownServers = [];
  56. // In set status
  57. this.set = {};
  58. // Status
  59. this.maxElectionId = null;
  60. this.maxSetVersion = 0;
  61. // Description of the Replicaset
  62. this.replicasetDescription = {
  63. topologyType: 'Unknown',
  64. servers: []
  65. };
  66. this.logicalSessionTimeoutMinutes = undefined;
  67. };
  68. inherits(ReplSetState, EventEmitter);
  69. ReplSetState.prototype.hasPrimaryAndSecondary = function() {
  70. return this.primary != null && this.secondaries.length > 0;
  71. };
  72. ReplSetState.prototype.hasPrimaryOrSecondary = function() {
  73. return this.hasPrimary() || this.hasSecondary();
  74. };
  75. ReplSetState.prototype.hasPrimary = function() {
  76. return this.primary != null;
  77. };
  78. ReplSetState.prototype.hasSecondary = function() {
  79. return this.secondaries.length > 0;
  80. };
  81. ReplSetState.prototype.get = function(host) {
  82. var servers = this.allServers();
  83. for (var i = 0; i < servers.length; i++) {
  84. if (servers[i].name.toLowerCase() === host.toLowerCase()) {
  85. return servers[i];
  86. }
  87. }
  88. return null;
  89. };
  90. ReplSetState.prototype.allServers = function(options) {
  91. options = options || {};
  92. var servers = this.primary ? [this.primary] : [];
  93. servers = servers.concat(this.secondaries);
  94. if (!options.ignoreArbiters) servers = servers.concat(this.arbiters);
  95. servers = servers.concat(this.passives);
  96. return servers;
  97. };
  98. ReplSetState.prototype.destroy = function(options, callback) {
  99. const serversToDestroy = this.secondaries
  100. .concat(this.arbiters)
  101. .concat(this.passives)
  102. .concat(this.ghosts);
  103. if (this.primary) serversToDestroy.push(this.primary);
  104. let serverCount = serversToDestroy.length;
  105. const serverDestroyed = () => {
  106. serverCount--;
  107. if (serverCount > 0) {
  108. return;
  109. }
  110. // Clear out the complete state
  111. this.secondaries = [];
  112. this.arbiters = [];
  113. this.passives = [];
  114. this.ghosts = [];
  115. this.unknownServers = [];
  116. this.set = {};
  117. this.primary = null;
  118. // Emit the topology changed
  119. emitTopologyDescriptionChanged(this);
  120. if (typeof callback === 'function') {
  121. callback(null, null);
  122. }
  123. };
  124. if (serverCount === 0) {
  125. serverDestroyed();
  126. return;
  127. }
  128. serversToDestroy.forEach(server => server.destroy(options, serverDestroyed));
  129. };
  130. ReplSetState.prototype.remove = function(server, options) {
  131. options = options || {};
  132. // Get the server name and lowerCase it
  133. var serverName = server.name.toLowerCase();
  134. // Only remove if the current server is not connected
  135. var servers = this.primary ? [this.primary] : [];
  136. servers = servers.concat(this.secondaries);
  137. servers = servers.concat(this.arbiters);
  138. servers = servers.concat(this.passives);
  139. // Check if it's active and this is just a failed connection attempt
  140. for (var i = 0; i < servers.length; i++) {
  141. if (
  142. !options.force &&
  143. servers[i].equals(server) &&
  144. servers[i].isConnected &&
  145. servers[i].isConnected()
  146. ) {
  147. return;
  148. }
  149. }
  150. // If we have it in the set remove it
  151. if (this.set[serverName]) {
  152. this.set[serverName].type = ServerType.Unknown;
  153. this.set[serverName].electionId = null;
  154. this.set[serverName].setName = null;
  155. this.set[serverName].setVersion = null;
  156. }
  157. // Remove type
  158. var removeType = null;
  159. // Remove from any lists
  160. if (this.primary && this.primary.equals(server)) {
  161. this.primary = null;
  162. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  163. removeType = 'primary';
  164. }
  165. // Remove from any other server lists
  166. removeType = removeFrom(server, this.secondaries) ? 'secondary' : removeType;
  167. removeType = removeFrom(server, this.arbiters) ? 'arbiter' : removeType;
  168. removeType = removeFrom(server, this.passives) ? 'secondary' : removeType;
  169. removeFrom(server, this.ghosts);
  170. removeFrom(server, this.unknownServers);
  171. // Push to unknownServers
  172. this.unknownServers.push(serverName);
  173. // Do we have a removeType
  174. if (removeType) {
  175. this.emit('left', removeType, server);
  176. }
  177. };
  178. const isArbiter = ismaster => ismaster.arbiterOnly && ismaster.setName;
  179. ReplSetState.prototype.update = function(server) {
  180. var self = this;
  181. // Get the current ismaster
  182. var ismaster = server.lastIsMaster();
  183. // Get the server name and lowerCase it
  184. var serverName = server.name.toLowerCase();
  185. //
  186. // Add any hosts
  187. //
  188. if (ismaster) {
  189. // Join all the possible new hosts
  190. var hosts = Array.isArray(ismaster.hosts) ? ismaster.hosts : [];
  191. hosts = hosts.concat(Array.isArray(ismaster.arbiters) ? ismaster.arbiters : []);
  192. hosts = hosts.concat(Array.isArray(ismaster.passives) ? ismaster.passives : []);
  193. hosts = hosts.map(function(s) {
  194. return s.toLowerCase();
  195. });
  196. // Add all hosts as unknownServers
  197. for (var i = 0; i < hosts.length; i++) {
  198. // Add to the list of unknown server
  199. if (
  200. this.unknownServers.indexOf(hosts[i]) === -1 &&
  201. (!this.set[hosts[i]] || this.set[hosts[i]].type === ServerType.Unknown)
  202. ) {
  203. this.unknownServers.push(hosts[i].toLowerCase());
  204. }
  205. if (!this.set[hosts[i]]) {
  206. this.set[hosts[i]] = {
  207. type: ServerType.Unknown,
  208. electionId: null,
  209. setName: null,
  210. setVersion: null
  211. };
  212. }
  213. }
  214. }
  215. //
  216. // Unknown server
  217. //
  218. if (!ismaster && !inList(ismaster, server, this.unknownServers)) {
  219. self.set[serverName] = {
  220. type: ServerType.Unknown,
  221. setVersion: null,
  222. electionId: null,
  223. setName: null
  224. };
  225. // Update set information about the server instance
  226. self.set[serverName].type = ServerType.Unknown;
  227. self.set[serverName].electionId = ismaster ? ismaster.electionId : ismaster;
  228. self.set[serverName].setName = ismaster ? ismaster.setName : ismaster;
  229. self.set[serverName].setVersion = ismaster ? ismaster.setVersion : ismaster;
  230. if (self.unknownServers.indexOf(server.name) === -1) {
  231. self.unknownServers.push(serverName);
  232. }
  233. // Set the topology
  234. return false;
  235. }
  236. // Update logicalSessionTimeoutMinutes
  237. if (ismaster.logicalSessionTimeoutMinutes !== undefined && !isArbiter(ismaster)) {
  238. if (
  239. self.logicalSessionTimeoutMinutes === undefined ||
  240. ismaster.logicalSessionTimeoutMinutes === null
  241. ) {
  242. self.logicalSessionTimeoutMinutes = ismaster.logicalSessionTimeoutMinutes;
  243. } else {
  244. self.logicalSessionTimeoutMinutes = Math.min(
  245. self.logicalSessionTimeoutMinutes,
  246. ismaster.logicalSessionTimeoutMinutes
  247. );
  248. }
  249. }
  250. //
  251. // Is this a mongos
  252. //
  253. if (ismaster && ismaster.msg === 'isdbgrid') {
  254. if (this.primary && this.primary.name === serverName) {
  255. this.primary = null;
  256. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  257. }
  258. return false;
  259. }
  260. // A RSGhost instance
  261. if (ismaster.isreplicaset) {
  262. self.set[serverName] = {
  263. type: ServerType.RSGhost,
  264. setVersion: null,
  265. electionId: null,
  266. setName: ismaster.setName
  267. };
  268. if (this.primary && this.primary.name === serverName) {
  269. this.primary = null;
  270. }
  271. // Set the topology
  272. this.topologyType = this.primary
  273. ? TopologyType.ReplicaSetWithPrimary
  274. : TopologyType.ReplicaSetNoPrimary;
  275. if (ismaster.setName) this.setName = ismaster.setName;
  276. // Set the topology
  277. return false;
  278. }
  279. // A RSOther instance
  280. if (
  281. (ismaster.setName && ismaster.hidden) ||
  282. (ismaster.setName &&
  283. !ismaster.ismaster &&
  284. !ismaster.secondary &&
  285. !ismaster.arbiterOnly &&
  286. !ismaster.passive)
  287. ) {
  288. self.set[serverName] = {
  289. type: ServerType.RSOther,
  290. setVersion: null,
  291. electionId: null,
  292. setName: ismaster.setName
  293. };
  294. // Set the topology
  295. this.topologyType = this.primary
  296. ? TopologyType.ReplicaSetWithPrimary
  297. : TopologyType.ReplicaSetNoPrimary;
  298. if (ismaster.setName) this.setName = ismaster.setName;
  299. return false;
  300. }
  301. //
  302. // Standalone server, destroy and return
  303. //
  304. if (ismaster && ismaster.ismaster && !ismaster.setName) {
  305. this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
  306. this.remove(server, { force: true });
  307. return false;
  308. }
  309. //
  310. // Server in maintanance mode
  311. //
  312. if (ismaster && !ismaster.ismaster && !ismaster.secondary && !ismaster.arbiterOnly) {
  313. this.remove(server, { force: true });
  314. return false;
  315. }
  316. //
  317. // If the .me field does not match the passed in server
  318. //
  319. if (ismaster.me && ismaster.me.toLowerCase() !== serverName) {
  320. if (this.logger.isWarn()) {
  321. this.logger.warn(
  322. f(
  323. 'the seedlist server was removed due to its address %s not matching its ismaster.me address %s',
  324. server.name,
  325. ismaster.me
  326. )
  327. );
  328. }
  329. // Delete from the set
  330. delete this.set[serverName];
  331. // Delete unknown servers
  332. removeFrom(server, self.unknownServers);
  333. // Destroy the instance
  334. server.destroy();
  335. // Set the type of topology we have
  336. if (this.primary && !this.primary.equals(server)) {
  337. this.topologyType = TopologyType.ReplicaSetWithPrimary;
  338. } else {
  339. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  340. }
  341. //
  342. // We have a potential primary
  343. //
  344. if (!this.primary && ismaster.primary) {
  345. this.set[ismaster.primary.toLowerCase()] = {
  346. type: ServerType.PossiblePrimary,
  347. setName: null,
  348. electionId: null,
  349. setVersion: null
  350. };
  351. }
  352. return false;
  353. }
  354. //
  355. // Primary handling
  356. //
  357. if (!this.primary && ismaster.ismaster && ismaster.setName) {
  358. var ismasterElectionId = server.lastIsMaster().electionId;
  359. if (this.setName && this.setName !== ismaster.setName) {
  360. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  361. return new MongoError(
  362. f(
  363. 'setName from ismaster does not match provided connection setName [%s] != [%s]',
  364. ismaster.setName,
  365. this.setName
  366. )
  367. );
  368. }
  369. if (!this.maxElectionId && ismasterElectionId) {
  370. this.maxElectionId = ismasterElectionId;
  371. } else if (this.maxElectionId && ismasterElectionId) {
  372. var result = compareObjectIds(this.maxElectionId, ismasterElectionId);
  373. // Get the electionIds
  374. var ismasterSetVersion = server.lastIsMaster().setVersion;
  375. if (result === 1) {
  376. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  377. return false;
  378. } else if (result === 0 && ismasterSetVersion) {
  379. if (ismasterSetVersion < this.maxSetVersion) {
  380. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  381. return false;
  382. }
  383. }
  384. this.maxSetVersion = ismasterSetVersion;
  385. this.maxElectionId = ismasterElectionId;
  386. }
  387. // Hande normalization of server names
  388. var normalizedHosts = ismaster.hosts.map(function(x) {
  389. return x.toLowerCase();
  390. });
  391. var locationIndex = normalizedHosts.indexOf(serverName);
  392. // Validate that the server exists in the host list
  393. if (locationIndex !== -1) {
  394. self.primary = server;
  395. self.set[serverName] = {
  396. type: ServerType.RSPrimary,
  397. setVersion: ismaster.setVersion,
  398. electionId: ismaster.electionId,
  399. setName: ismaster.setName
  400. };
  401. // Set the topology
  402. this.topologyType = TopologyType.ReplicaSetWithPrimary;
  403. if (ismaster.setName) this.setName = ismaster.setName;
  404. removeFrom(server, self.unknownServers);
  405. removeFrom(server, self.secondaries);
  406. removeFrom(server, self.passives);
  407. self.emit('joined', 'primary', server);
  408. } else {
  409. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  410. }
  411. emitTopologyDescriptionChanged(self);
  412. return true;
  413. } else if (ismaster.ismaster && ismaster.setName) {
  414. // Get the electionIds
  415. var currentElectionId = self.set[self.primary.name.toLowerCase()].electionId;
  416. var currentSetVersion = self.set[self.primary.name.toLowerCase()].setVersion;
  417. var currentSetName = self.set[self.primary.name.toLowerCase()].setName;
  418. ismasterElectionId = server.lastIsMaster().electionId;
  419. ismasterSetVersion = server.lastIsMaster().setVersion;
  420. var ismasterSetName = server.lastIsMaster().setName;
  421. // Is it the same server instance
  422. if (this.primary.equals(server) && currentSetName === ismasterSetName) {
  423. return false;
  424. }
  425. // If we do not have the same rs name
  426. if (currentSetName && currentSetName !== ismasterSetName) {
  427. if (!this.primary.equals(server)) {
  428. this.topologyType = TopologyType.ReplicaSetWithPrimary;
  429. } else {
  430. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  431. }
  432. return false;
  433. }
  434. // Check if we need to replace the server
  435. if (currentElectionId && ismasterElectionId) {
  436. result = compareObjectIds(currentElectionId, ismasterElectionId);
  437. if (result === 1) {
  438. return false;
  439. } else if (result === 0 && currentSetVersion > ismasterSetVersion) {
  440. return false;
  441. }
  442. } else if (!currentElectionId && ismasterElectionId && ismasterSetVersion) {
  443. if (ismasterSetVersion < this.maxSetVersion) {
  444. return false;
  445. }
  446. }
  447. if (!this.maxElectionId && ismasterElectionId) {
  448. this.maxElectionId = ismasterElectionId;
  449. } else if (this.maxElectionId && ismasterElectionId) {
  450. result = compareObjectIds(this.maxElectionId, ismasterElectionId);
  451. if (result === 1) {
  452. return false;
  453. } else if (result === 0 && currentSetVersion && ismasterSetVersion) {
  454. if (ismasterSetVersion < this.maxSetVersion) {
  455. return false;
  456. }
  457. } else {
  458. if (ismasterSetVersion < this.maxSetVersion) {
  459. return false;
  460. }
  461. }
  462. this.maxElectionId = ismasterElectionId;
  463. this.maxSetVersion = ismasterSetVersion;
  464. } else {
  465. this.maxSetVersion = ismasterSetVersion;
  466. }
  467. // Modify the entry to unknown
  468. self.set[self.primary.name.toLowerCase()] = {
  469. type: ServerType.Unknown,
  470. setVersion: null,
  471. electionId: null,
  472. setName: null
  473. };
  474. // Signal primary left
  475. self.emit('left', 'primary', this.primary);
  476. // Destroy the instance
  477. self.primary.destroy();
  478. // Set the new instance
  479. self.primary = server;
  480. // Set the set information
  481. self.set[serverName] = {
  482. type: ServerType.RSPrimary,
  483. setVersion: ismaster.setVersion,
  484. electionId: ismaster.electionId,
  485. setName: ismaster.setName
  486. };
  487. // Set the topology
  488. this.topologyType = TopologyType.ReplicaSetWithPrimary;
  489. if (ismaster.setName) this.setName = ismaster.setName;
  490. removeFrom(server, self.unknownServers);
  491. removeFrom(server, self.secondaries);
  492. removeFrom(server, self.passives);
  493. self.emit('joined', 'primary', server);
  494. emitTopologyDescriptionChanged(self);
  495. return true;
  496. }
  497. // A possible instance
  498. if (!this.primary && ismaster.primary) {
  499. self.set[ismaster.primary.toLowerCase()] = {
  500. type: ServerType.PossiblePrimary,
  501. setVersion: null,
  502. electionId: null,
  503. setName: null
  504. };
  505. }
  506. //
  507. // Secondary handling
  508. //
  509. if (
  510. ismaster.secondary &&
  511. ismaster.setName &&
  512. !inList(ismaster, server, this.secondaries) &&
  513. this.setName &&
  514. this.setName === ismaster.setName
  515. ) {
  516. addToList(self, ServerType.RSSecondary, ismaster, server, this.secondaries);
  517. // Set the topology
  518. this.topologyType = this.primary
  519. ? TopologyType.ReplicaSetWithPrimary
  520. : TopologyType.ReplicaSetNoPrimary;
  521. if (ismaster.setName) this.setName = ismaster.setName;
  522. removeFrom(server, self.unknownServers);
  523. // Remove primary
  524. if (this.primary && this.primary.name.toLowerCase() === serverName) {
  525. server.destroy();
  526. this.primary = null;
  527. self.emit('left', 'primary', server);
  528. }
  529. // Emit secondary joined replicaset
  530. self.emit('joined', 'secondary', server);
  531. emitTopologyDescriptionChanged(self);
  532. return true;
  533. }
  534. //
  535. // Arbiter handling
  536. //
  537. if (
  538. isArbiter(ismaster) &&
  539. !inList(ismaster, server, this.arbiters) &&
  540. this.setName &&
  541. this.setName === ismaster.setName
  542. ) {
  543. addToList(self, ServerType.RSArbiter, ismaster, server, this.arbiters);
  544. // Set the topology
  545. this.topologyType = this.primary
  546. ? TopologyType.ReplicaSetWithPrimary
  547. : TopologyType.ReplicaSetNoPrimary;
  548. if (ismaster.setName) this.setName = ismaster.setName;
  549. removeFrom(server, self.unknownServers);
  550. self.emit('joined', 'arbiter', server);
  551. emitTopologyDescriptionChanged(self);
  552. return true;
  553. }
  554. //
  555. // Passive handling
  556. //
  557. if (
  558. ismaster.passive &&
  559. ismaster.setName &&
  560. !inList(ismaster, server, this.passives) &&
  561. this.setName &&
  562. this.setName === ismaster.setName
  563. ) {
  564. addToList(self, ServerType.RSSecondary, ismaster, server, this.passives);
  565. // Set the topology
  566. this.topologyType = this.primary
  567. ? TopologyType.ReplicaSetWithPrimary
  568. : TopologyType.ReplicaSetNoPrimary;
  569. if (ismaster.setName) this.setName = ismaster.setName;
  570. removeFrom(server, self.unknownServers);
  571. // Remove primary
  572. if (this.primary && this.primary.name.toLowerCase() === serverName) {
  573. server.destroy();
  574. this.primary = null;
  575. self.emit('left', 'primary', server);
  576. }
  577. self.emit('joined', 'secondary', server);
  578. emitTopologyDescriptionChanged(self);
  579. return true;
  580. }
  581. //
  582. // Remove the primary
  583. //
  584. if (this.set[serverName] && this.set[serverName].type === ServerType.RSPrimary) {
  585. self.emit('left', 'primary', this.primary);
  586. this.primary.destroy();
  587. this.primary = null;
  588. this.topologyType = TopologyType.ReplicaSetNoPrimary;
  589. return false;
  590. }
  591. this.topologyType = this.primary
  592. ? TopologyType.ReplicaSetWithPrimary
  593. : TopologyType.ReplicaSetNoPrimary;
  594. return false;
  595. };
  596. /**
  597. * Recalculate single server max staleness
  598. * @method
  599. */
  600. ReplSetState.prototype.updateServerMaxStaleness = function(server, haInterval) {
  601. // Locate the max secondary lastwrite
  602. var max = 0;
  603. // Go over all secondaries
  604. for (var i = 0; i < this.secondaries.length; i++) {
  605. max = Math.max(max, this.secondaries[i].lastWriteDate);
  606. }
  607. // Perform this servers staleness calculation
  608. if (server.ismaster.maxWireVersion >= 5 && server.ismaster.secondary && this.hasPrimary()) {
  609. server.staleness =
  610. server.lastUpdateTime -
  611. server.lastWriteDate -
  612. (this.primary.lastUpdateTime - this.primary.lastWriteDate) +
  613. haInterval;
  614. } else if (server.ismaster.maxWireVersion >= 5 && server.ismaster.secondary) {
  615. server.staleness = max - server.lastWriteDate + haInterval;
  616. }
  617. };
  618. /**
  619. * Recalculate all the staleness values for secodaries
  620. * @method
  621. */
  622. ReplSetState.prototype.updateSecondariesMaxStaleness = function(haInterval) {
  623. for (var i = 0; i < this.secondaries.length; i++) {
  624. this.updateServerMaxStaleness(this.secondaries[i], haInterval);
  625. }
  626. };
  627. /**
  628. * Pick a server by the passed in ReadPreference
  629. * @method
  630. * @param {ReadPreference} readPreference The ReadPreference instance to use
  631. */
  632. ReplSetState.prototype.pickServer = function(readPreference) {
  633. // If no read Preference set to primary by default
  634. readPreference = readPreference || ReadPreference.primary;
  635. // maxStalenessSeconds is not allowed with a primary read
  636. if (readPreference.preference === 'primary' && readPreference.maxStalenessSeconds != null) {
  637. return new MongoError('primary readPreference incompatible with maxStalenessSeconds');
  638. }
  639. // Check if we have any non compatible servers for maxStalenessSeconds
  640. var allservers = this.primary ? [this.primary] : [];
  641. allservers = allservers.concat(this.secondaries);
  642. // Does any of the servers not support the right wire protocol version
  643. // for maxStalenessSeconds when maxStalenessSeconds specified on readPreference. Then error out
  644. if (readPreference.maxStalenessSeconds != null) {
  645. for (var i = 0; i < allservers.length; i++) {
  646. if (allservers[i].ismaster.maxWireVersion < 5) {
  647. return new MongoError(
  648. 'maxStalenessSeconds not supported by at least one of the replicaset members'
  649. );
  650. }
  651. }
  652. }
  653. // Do we have the nearest readPreference
  654. if (readPreference.preference === 'nearest' && readPreference.maxStalenessSeconds == null) {
  655. return pickNearest(this, readPreference);
  656. } else if (
  657. readPreference.preference === 'nearest' &&
  658. readPreference.maxStalenessSeconds != null
  659. ) {
  660. return pickNearestMaxStalenessSeconds(this, readPreference);
  661. }
  662. // Get all the secondaries
  663. var secondaries = this.secondaries;
  664. // Check if we can satisfy and of the basic read Preferences
  665. if (readPreference.equals(ReadPreference.secondary) && secondaries.length === 0) {
  666. return new MongoError('no secondary server available');
  667. }
  668. if (
  669. readPreference.equals(ReadPreference.secondaryPreferred) &&
  670. secondaries.length === 0 &&
  671. this.primary == null
  672. ) {
  673. return new MongoError('no secondary or primary server available');
  674. }
  675. if (readPreference.equals(ReadPreference.primary) && this.primary == null) {
  676. return new MongoError('no primary server available');
  677. }
  678. // Secondary preferred or just secondaries
  679. if (
  680. readPreference.equals(ReadPreference.secondaryPreferred) ||
  681. readPreference.equals(ReadPreference.secondary)
  682. ) {
  683. if (secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
  684. // Pick nearest of any other servers available
  685. var server = pickNearest(this, readPreference);
  686. // No server in the window return primary
  687. if (server) {
  688. return server;
  689. }
  690. } else if (secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
  691. // Pick nearest of any other servers available
  692. server = pickNearestMaxStalenessSeconds(this, readPreference);
  693. // No server in the window return primary
  694. if (server) {
  695. return server;
  696. }
  697. }
  698. if (readPreference.equals(ReadPreference.secondaryPreferred)) {
  699. return this.primary;
  700. }
  701. return null;
  702. }
  703. // Primary preferred
  704. if (readPreference.equals(ReadPreference.primaryPreferred)) {
  705. server = null;
  706. // We prefer the primary if it's available
  707. if (this.primary) {
  708. return this.primary;
  709. }
  710. // Pick a secondary
  711. if (secondaries.length > 0 && readPreference.maxStalenessSeconds == null) {
  712. server = pickNearest(this, readPreference);
  713. } else if (secondaries.length > 0 && readPreference.maxStalenessSeconds != null) {
  714. server = pickNearestMaxStalenessSeconds(this, readPreference);
  715. }
  716. // Did we find a server
  717. if (server) return server;
  718. }
  719. // Return the primary
  720. return this.primary;
  721. };
  722. //
  723. // Filter serves by tags
  724. var filterByTags = function(readPreference, servers) {
  725. if (readPreference.tags == null) return servers;
  726. var filteredServers = [];
  727. var tagsArray = Array.isArray(readPreference.tags) ? readPreference.tags : [readPreference.tags];
  728. // Iterate over the tags
  729. for (var j = 0; j < tagsArray.length; j++) {
  730. var tags = tagsArray[j];
  731. // Iterate over all the servers
  732. for (var i = 0; i < servers.length; i++) {
  733. var serverTag = servers[i].lastIsMaster().tags || {};
  734. // Did we find the a matching server
  735. var found = true;
  736. // Check if the server is valid
  737. for (var name in tags) {
  738. if (serverTag[name] !== tags[name]) {
  739. found = false;
  740. }
  741. }
  742. // Add to candidate list
  743. if (found) {
  744. filteredServers.push(servers[i]);
  745. }
  746. }
  747. }
  748. // Returned filtered servers
  749. return filteredServers;
  750. };
  751. function pickNearestMaxStalenessSeconds(self, readPreference) {
  752. // Only get primary and secondaries as seeds
  753. var servers = [];
  754. // Get the maxStalenessMS
  755. var maxStalenessMS = readPreference.maxStalenessSeconds * 1000;
  756. // Check if the maxStalenessMS > 90 seconds
  757. if (maxStalenessMS < 90 * 1000) {
  758. return new MongoError('maxStalenessSeconds must be set to at least 90 seconds');
  759. }
  760. // Add primary to list if not a secondary read preference
  761. if (
  762. self.primary &&
  763. readPreference.preference !== 'secondary' &&
  764. readPreference.preference !== 'secondaryPreferred'
  765. ) {
  766. servers.push(self.primary);
  767. }
  768. // Add all the secondaries
  769. for (var i = 0; i < self.secondaries.length; i++) {
  770. servers.push(self.secondaries[i]);
  771. }
  772. // If we have a secondaryPreferred readPreference and no server add the primary
  773. if (self.primary && servers.length === 0 && readPreference.preference !== 'secondaryPreferred') {
  774. servers.push(self.primary);
  775. }
  776. // Filter by tags
  777. servers = filterByTags(readPreference, servers);
  778. // Filter by latency
  779. servers = servers.filter(function(s) {
  780. return s.staleness <= maxStalenessMS;
  781. });
  782. // Sort by time
  783. servers.sort(function(a, b) {
  784. return a.lastIsMasterMS - b.lastIsMasterMS;
  785. });
  786. // No servers, default to primary
  787. if (servers.length === 0) {
  788. return null;
  789. }
  790. // Ensure index does not overflow the number of available servers
  791. self.index = self.index % servers.length;
  792. // Get the server
  793. var server = servers[self.index];
  794. // Add to the index
  795. self.index = self.index + 1;
  796. // Return the first server of the sorted and filtered list
  797. return server;
  798. }
  799. function pickNearest(self, readPreference) {
  800. // Only get primary and secondaries as seeds
  801. var servers = [];
  802. // Add primary to list if not a secondary read preference
  803. if (
  804. self.primary &&
  805. readPreference.preference !== 'secondary' &&
  806. readPreference.preference !== 'secondaryPreferred'
  807. ) {
  808. servers.push(self.primary);
  809. }
  810. // Add all the secondaries
  811. for (var i = 0; i < self.secondaries.length; i++) {
  812. servers.push(self.secondaries[i]);
  813. }
  814. // If we have a secondaryPreferred readPreference and no server add the primary
  815. if (servers.length === 0 && self.primary && readPreference.preference !== 'secondaryPreferred') {
  816. servers.push(self.primary);
  817. }
  818. // Filter by tags
  819. servers = filterByTags(readPreference, servers);
  820. // Sort by time
  821. servers.sort(function(a, b) {
  822. return a.lastIsMasterMS - b.lastIsMasterMS;
  823. });
  824. // Locate lowest time (picked servers are lowest time + acceptable Latency margin)
  825. var lowest = servers.length > 0 ? servers[0].lastIsMasterMS : 0;
  826. // Filter by latency
  827. servers = servers.filter(function(s) {
  828. return s.lastIsMasterMS <= lowest + self.acceptableLatency;
  829. });
  830. // No servers, default to primary
  831. if (servers.length === 0) {
  832. return null;
  833. }
  834. // Ensure index does not overflow the number of available servers
  835. self.index = self.index % servers.length;
  836. // Get the server
  837. var server = servers[self.index];
  838. // Add to the index
  839. self.index = self.index + 1;
  840. // Return the first server of the sorted and filtered list
  841. return server;
  842. }
  843. function inList(ismaster, server, list) {
  844. for (var i = 0; i < list.length; i++) {
  845. if (list[i] && list[i].name && list[i].name.toLowerCase() === server.name.toLowerCase())
  846. return true;
  847. }
  848. return false;
  849. }
  850. function addToList(self, type, ismaster, server, list) {
  851. var serverName = server.name.toLowerCase();
  852. // Update set information about the server instance
  853. self.set[serverName].type = type;
  854. self.set[serverName].electionId = ismaster ? ismaster.electionId : ismaster;
  855. self.set[serverName].setName = ismaster ? ismaster.setName : ismaster;
  856. self.set[serverName].setVersion = ismaster ? ismaster.setVersion : ismaster;
  857. // Add to the list
  858. list.push(server);
  859. }
  860. function compareObjectIds(id1, id2) {
  861. var a = Buffer.from(id1.toHexString(), 'hex');
  862. var b = Buffer.from(id2.toHexString(), 'hex');
  863. if (a === b) {
  864. return 0;
  865. }
  866. if (typeof Buffer.compare === 'function') {
  867. return Buffer.compare(a, b);
  868. }
  869. var x = a.length;
  870. var y = b.length;
  871. var len = Math.min(x, y);
  872. for (var i = 0; i < len; i++) {
  873. if (a[i] !== b[i]) {
  874. break;
  875. }
  876. }
  877. if (i !== len) {
  878. x = a[i];
  879. y = b[i];
  880. }
  881. return x < y ? -1 : y < x ? 1 : 0;
  882. }
  883. function removeFrom(server, list) {
  884. for (var i = 0; i < list.length; i++) {
  885. if (list[i].equals && list[i].equals(server)) {
  886. list.splice(i, 1);
  887. return true;
  888. } else if (typeof list[i] === 'string' && list[i].toLowerCase() === server.name.toLowerCase()) {
  889. list.splice(i, 1);
  890. return true;
  891. }
  892. }
  893. return false;
  894. }
  895. function emitTopologyDescriptionChanged(self) {
  896. if (self.listeners('topologyDescriptionChanged').length > 0) {
  897. var topology = 'Unknown';
  898. var setName = self.setName;
  899. if (self.hasPrimaryAndSecondary()) {
  900. topology = 'ReplicaSetWithPrimary';
  901. } else if (!self.hasPrimary() && self.hasSecondary()) {
  902. topology = 'ReplicaSetNoPrimary';
  903. }
  904. // Generate description
  905. var description = {
  906. topologyType: topology,
  907. setName: setName,
  908. servers: []
  909. };
  910. // Add the primary to the list
  911. if (self.hasPrimary()) {
  912. var desc = self.primary.getDescription();
  913. desc.type = 'RSPrimary';
  914. description.servers.push(desc);
  915. }
  916. // Add all the secondaries
  917. description.servers = description.servers.concat(
  918. self.secondaries.map(function(x) {
  919. var description = x.getDescription();
  920. description.type = 'RSSecondary';
  921. return description;
  922. })
  923. );
  924. // Add all the arbiters
  925. description.servers = description.servers.concat(
  926. self.arbiters.map(function(x) {
  927. var description = x.getDescription();
  928. description.type = 'RSArbiter';
  929. return description;
  930. })
  931. );
  932. // Add all the passives
  933. description.servers = description.servers.concat(
  934. self.passives.map(function(x) {
  935. var description = x.getDescription();
  936. description.type = 'RSSecondary';
  937. return description;
  938. })
  939. );
  940. // Get the diff
  941. var diffResult = diff(self.replicasetDescription, description);
  942. // Create the result
  943. var result = {
  944. topologyId: self.id,
  945. previousDescription: self.replicasetDescription,
  946. newDescription: description,
  947. diff: diffResult
  948. };
  949. // Emit the topologyDescription change
  950. // if(diffResult.servers.length > 0) {
  951. self.emit('topologyDescriptionChanged', result);
  952. // }
  953. // Set the new description
  954. self.replicasetDescription = description;
  955. }
  956. }
  957. module.exports = ReplSetState;