Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

replset_state.js 31KB

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