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.

server_selectors.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. 'use strict';
  2. const ServerType = require('./server_description').ServerType;
  3. const TopologyType = require('./topology_description').TopologyType;
  4. const ReadPreference = require('../topologies/read_preference');
  5. const MongoError = require('../error').MongoError;
  6. // max staleness constants
  7. const IDLE_WRITE_PERIOD = 10000;
  8. const SMALLEST_MAX_STALENESS_SECONDS = 90;
  9. function writableServerSelector() {
  10. return function(topologyDescription, servers) {
  11. return latencyWindowReducer(topologyDescription, servers.filter(s => s.isWritable));
  12. };
  13. }
  14. // reducers
  15. function maxStalenessReducer(readPreference, topologyDescription, servers) {
  16. if (readPreference.maxStalenessSeconds == null || readPreference.maxStalenessSeconds < 0) {
  17. return servers;
  18. }
  19. const maxStaleness = readPreference.maxStalenessSeconds;
  20. const maxStalenessVariance =
  21. (topologyDescription.heartbeatFrequencyMS + IDLE_WRITE_PERIOD) / 1000;
  22. if (maxStaleness < maxStalenessVariance) {
  23. throw MongoError(`maxStalenessSeconds must be at least ${maxStalenessVariance} seconds`);
  24. }
  25. if (maxStaleness < SMALLEST_MAX_STALENESS_SECONDS) {
  26. throw new MongoError(
  27. `maxStalenessSeconds must be at least ${SMALLEST_MAX_STALENESS_SECONDS} seconds`
  28. );
  29. }
  30. if (topologyDescription.type === TopologyType.ReplicaSetWithPrimary) {
  31. const primary = servers.filter(primaryFilter)[0];
  32. return servers.reduce((result, server) => {
  33. const stalenessMS =
  34. server.lastUpdateTime -
  35. server.lastWriteDate -
  36. (primary.lastUpdateTime - primary.lastWriteDate) +
  37. topologyDescription.heartbeatFrequencyMS;
  38. const staleness = stalenessMS / 1000;
  39. if (staleness <= readPreference.maxStalenessSeconds) result.push(server);
  40. return result;
  41. }, []);
  42. } else if (topologyDescription.type === TopologyType.ReplicaSetNoPrimary) {
  43. const sMax = servers.reduce((max, s) => (s.lastWriteDate > max.lastWriteDate ? s : max));
  44. return servers.reduce((result, server) => {
  45. const stalenessMS =
  46. sMax.lastWriteDate - server.lastWriteDate + topologyDescription.heartbeatFrequencyMS;
  47. const staleness = stalenessMS / 1000;
  48. if (staleness <= readPreference.maxStalenessSeconds) result.push(server);
  49. return result;
  50. }, []);
  51. }
  52. return servers;
  53. }
  54. function tagSetMatch(tagSet, serverTags) {
  55. const keys = Object.keys(tagSet);
  56. const serverTagKeys = Object.keys(serverTags);
  57. for (let i = 0; i < keys.length; ++i) {
  58. const key = keys[i];
  59. if (serverTagKeys.indexOf(key) === -1 || serverTags[key] !== tagSet[key]) {
  60. return false;
  61. }
  62. }
  63. return true;
  64. }
  65. function tagSetReducer(readPreference, servers) {
  66. if (
  67. readPreference.tags == null ||
  68. (Array.isArray(readPreference.tags) && readPreference.tags.length === 0)
  69. ) {
  70. return servers;
  71. }
  72. for (let i = 0; i < readPreference.tags.length; ++i) {
  73. const tagSet = readPreference.tags[i];
  74. const serversMatchingTagset = servers.reduce((matched, server) => {
  75. if (tagSetMatch(tagSet, server.tags)) matched.push(server);
  76. return matched;
  77. }, []);
  78. if (serversMatchingTagset.length) {
  79. return serversMatchingTagset;
  80. }
  81. }
  82. return [];
  83. }
  84. function latencyWindowReducer(topologyDescription, servers) {
  85. const low = servers.reduce(
  86. (min, server) => (min === -1 ? server.roundTripTime : Math.min(server.roundTripTime, min)),
  87. -1
  88. );
  89. const high = low + topologyDescription.localThresholdMS;
  90. return servers.reduce((result, server) => {
  91. if (server.roundTripTime <= high && server.roundTripTime >= low) result.push(server);
  92. return result;
  93. }, []);
  94. }
  95. // filters
  96. function primaryFilter(server) {
  97. return server.type === ServerType.RSPrimary;
  98. }
  99. function secondaryFilter(server) {
  100. return server.type === ServerType.RSSecondary;
  101. }
  102. function nearestFilter(server) {
  103. return server.type === ServerType.RSSecondary || server.type === ServerType.RSPrimary;
  104. }
  105. function knownFilter(server) {
  106. return server.type !== ServerType.Unknown;
  107. }
  108. function readPreferenceServerSelector(readPreference) {
  109. if (!readPreference.isValid()) {
  110. throw new TypeError('Invalid read preference specified');
  111. }
  112. return function(topologyDescription, servers) {
  113. const commonWireVersion = topologyDescription.commonWireVersion;
  114. if (
  115. commonWireVersion &&
  116. (readPreference.minWireVersion && readPreference.minWireVersion > commonWireVersion)
  117. ) {
  118. throw new MongoError(
  119. `Minimum wire version '${
  120. readPreference.minWireVersion
  121. }' required, but found '${commonWireVersion}'`
  122. );
  123. }
  124. if (
  125. topologyDescription.type === TopologyType.Single ||
  126. topologyDescription.type === TopologyType.Sharded
  127. ) {
  128. return latencyWindowReducer(topologyDescription, servers.filter(knownFilter));
  129. }
  130. if (readPreference.mode === ReadPreference.PRIMARY) {
  131. return servers.filter(primaryFilter);
  132. }
  133. if (readPreference.mode === ReadPreference.SECONDARY) {
  134. return latencyWindowReducer(
  135. topologyDescription,
  136. tagSetReducer(
  137. readPreference,
  138. maxStalenessReducer(readPreference, topologyDescription, servers)
  139. )
  140. ).filter(secondaryFilter);
  141. } else if (readPreference.mode === ReadPreference.NEAREST) {
  142. return latencyWindowReducer(
  143. topologyDescription,
  144. tagSetReducer(
  145. readPreference,
  146. maxStalenessReducer(readPreference, topologyDescription, servers)
  147. )
  148. ).filter(nearestFilter);
  149. } else if (readPreference.mode === ReadPreference.SECONDARY_PREFERRED) {
  150. const result = latencyWindowReducer(
  151. topologyDescription,
  152. tagSetReducer(
  153. readPreference,
  154. maxStalenessReducer(readPreference, topologyDescription, servers)
  155. )
  156. ).filter(secondaryFilter);
  157. return result.length === 0 ? servers.filter(primaryFilter) : result;
  158. } else if (readPreference.mode === ReadPreference.PRIMARY_PREFERRED) {
  159. const result = servers.filter(primaryFilter);
  160. if (result.length) {
  161. return result;
  162. }
  163. return latencyWindowReducer(
  164. topologyDescription,
  165. tagSetReducer(
  166. readPreference,
  167. maxStalenessReducer(readPreference, topologyDescription, servers)
  168. )
  169. ).filter(secondaryFilter);
  170. }
  171. };
  172. }
  173. module.exports = {
  174. writableServerSelector,
  175. readPreferenceServerSelector
  176. };