UNPKG

5.74 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const ArrayUtil_1 = require("../Utility/ArrayUtil");
4const moment = require("moment");
5const CurrentIndexAndNode_1 = require("../Http/CurrentIndexAndNode");
6const Timer_1 = require("../Primitives/Timer");
7const Exceptions_1 = require("../Exceptions");
8class NodeSelectorState {
9 constructor(topology) {
10 this.speedTestMode = 1;
11 this.topology = topology;
12 this.nodes = topology.nodes;
13 this.failures = ArrayUtil_1.ArrayUtil.range(topology.nodes.length, () => 0);
14 this.fastestRecords = ArrayUtil_1.ArrayUtil.range(topology.nodes.length, () => 0);
15 }
16}
17class NodeSelector {
18 constructor(topology) {
19 this._state = new NodeSelectorState(topology);
20 }
21 getTopology() {
22 return this._state.topology;
23 }
24 onFailedRequest(nodeIndex) {
25 const state = this._state;
26 if (nodeIndex < 0 || nodeIndex >= state.failures.length) {
27 return;
28 }
29 state.failures[nodeIndex]++;
30 }
31 onUpdateTopology(topology, forceUpdate = false) {
32 if (!topology) {
33 return false;
34 }
35 const stateEtag = this._state.topology.etag || 0;
36 const topologyEtag = this._state.topology.etag || 0;
37 if (stateEtag >= topologyEtag && !forceUpdate) {
38 return false;
39 }
40 this._state = new NodeSelectorState(topology);
41 return true;
42 }
43 getNodeBySessionId(sessionId) {
44 const state = this._state;
45 const index = sessionId % state.topology.nodes.length;
46 for (let i = index; i < state.failures.length; i++) {
47 if (state.failures[i] === 0
48 && state.nodes[i].serverRole === "Member") {
49 return new CurrentIndexAndNode_1.default(i, state.nodes[i]);
50 }
51 }
52 for (let i = 0; i < index; i++) {
53 if (state.failures[i] === 0
54 && state.nodes[i].serverRole === "Member") {
55 return new CurrentIndexAndNode_1.default(i, state.nodes[i]);
56 }
57 }
58 return this.getPreferredNode();
59 }
60 getPreferredNode() {
61 const state = this._state;
62 const stateFailures = state.failures;
63 const serverNodes = state.nodes;
64 const len = Math.min(serverNodes.length, stateFailures.length);
65 for (let i = 0; i < len; i++) {
66 if (stateFailures[i] === 0 && serverNodes[i].url) {
67 return new CurrentIndexAndNode_1.default(i, serverNodes[i]);
68 }
69 }
70 return NodeSelector._unlikelyEveryoneFaultedChoice(state);
71 }
72 static _unlikelyEveryoneFaultedChoice(state) {
73 if (state.nodes.length === 0) {
74 Exceptions_1.throwError("AllTopologyNodesDownException", "There are no nodes in the topology at all.");
75 }
76 return new CurrentIndexAndNode_1.default(0, state.nodes[0]);
77 }
78 getFastestNode() {
79 const state = this._state;
80 if (state.failures[state.fastest] === 0
81 && state.nodes[state.fastest].serverRole === "Member") {
82 return new CurrentIndexAndNode_1.default(state.fastest, state.nodes[state.fastest]);
83 }
84 this._switchToSpeedTestPhase();
85 return this.getPreferredNode();
86 }
87 restoreNodeIndex(nodeIndex) {
88 const state = this._state;
89 if (state.failures.length < nodeIndex) {
90 return;
91 }
92 state.failures[nodeIndex] = 0;
93 }
94 _throwEmptyTopology() {
95 Exceptions_1.throwError("InvalidOperationException", "Empty database topology, this shouldn't happen.");
96 }
97 _switchToSpeedTestPhase() {
98 const state = this._state;
99 if (state.speedTestMode === 0) {
100 state.speedTestMode = 1;
101 }
102 else {
103 return;
104 }
105 state.fastestRecords.fill(0);
106 state.speedTestMode++;
107 }
108 inSpeedTestPhase() {
109 return this._state.speedTestMode > 1;
110 }
111 recordFastest(index, node) {
112 const state = this._state;
113 const stateFastest = state.fastestRecords;
114 if (index < 0 || index >= stateFastest.length) {
115 return;
116 }
117 if (node !== state.nodes[index]) {
118 return;
119 }
120 if (++stateFastest[index] >= 10) {
121 this._selectFastest(state, index);
122 }
123 if (++state.speedTestMode <= state.nodes.length * 10) {
124 return;
125 }
126 const maxIndex = NodeSelector._findMaxIndex(state);
127 this._selectFastest(state, maxIndex);
128 }
129 static _findMaxIndex(state) {
130 const stateFastest = state.fastestRecords;
131 let maxIndex = 0;
132 let maxValue = 0;
133 for (let i = 0; i < stateFastest.length; i++) {
134 if (maxValue >= stateFastest[i]) {
135 continue;
136 }
137 maxIndex = i;
138 maxValue = stateFastest[i];
139 }
140 return maxIndex;
141 }
142 _selectFastest(state, index) {
143 state.fastest = index;
144 state.speedTestMode = 0;
145 const minuteMs = moment.duration(1, "m").asMilliseconds();
146 if (this._updateFastestNodeTimer !== null) {
147 this._updateFastestNodeTimer.change(minuteMs);
148 }
149 else {
150 this._updateFastestNodeTimer = new Timer_1.Timer(() => {
151 this._switchToSpeedTestPhase();
152 return Promise.resolve();
153 }, minuteMs);
154 }
155 }
156 scheduleSpeedTest() {
157 this._switchToSpeedTestPhase();
158 }
159}
160exports.NodeSelector = NodeSelector;