1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.setup = exports.PickFirstLoadBalancer = exports.PickFirstLoadBalancingConfig = void 0;
|
20 | const load_balancer_1 = require("./load-balancer");
|
21 | const connectivity_state_1 = require("./connectivity-state");
|
22 | const picker_1 = require("./picker");
|
23 | const subchannel_address_1 = require("./subchannel-address");
|
24 | const logging = require("./logging");
|
25 | const constants_1 = require("./constants");
|
26 | const TRACER_NAME = 'pick_first';
|
27 | function trace(text) {
|
28 | logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, text);
|
29 | }
|
30 | const TYPE_NAME = 'pick_first';
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | const CONNECTION_DELAY_INTERVAL_MS = 250;
|
36 | class PickFirstLoadBalancingConfig {
|
37 | getLoadBalancerName() {
|
38 | return TYPE_NAME;
|
39 | }
|
40 | constructor() { }
|
41 | toJsonObject() {
|
42 | return {
|
43 | [TYPE_NAME]: {},
|
44 | };
|
45 | }
|
46 |
|
47 | static createFromJson(obj) {
|
48 | return new PickFirstLoadBalancingConfig();
|
49 | }
|
50 | }
|
51 | exports.PickFirstLoadBalancingConfig = PickFirstLoadBalancingConfig;
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | class PickFirstPicker {
|
57 | constructor(subchannel) {
|
58 | this.subchannel = subchannel;
|
59 | }
|
60 | pick(pickArgs) {
|
61 | return {
|
62 | pickResultType: picker_1.PickResultType.COMPLETE,
|
63 | subchannel: this.subchannel,
|
64 | status: null,
|
65 | extraFilterFactories: [],
|
66 | onCallStarted: null,
|
67 | };
|
68 | }
|
69 | }
|
70 | class PickFirstLoadBalancer {
|
71 | |
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | constructor(channelControlHelper) {
|
79 | this.channelControlHelper = channelControlHelper;
|
80 | |
81 |
|
82 |
|
83 | this.latestAddressList = [];
|
84 | |
85 |
|
86 |
|
87 |
|
88 | this.subchannels = [];
|
89 | |
90 |
|
91 |
|
92 | this.currentState = connectivity_state_1.ConnectivityState.IDLE;
|
93 | |
94 |
|
95 |
|
96 |
|
97 | this.currentSubchannelIndex = 0;
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 | this.currentPick = null;
|
104 | this.triedAllSubchannels = false;
|
105 | this.subchannelStateCounts = {
|
106 | [connectivity_state_1.ConnectivityState.CONNECTING]: 0,
|
107 | [connectivity_state_1.ConnectivityState.IDLE]: 0,
|
108 | [connectivity_state_1.ConnectivityState.READY]: 0,
|
109 | [connectivity_state_1.ConnectivityState.SHUTDOWN]: 0,
|
110 | [connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE]: 0,
|
111 | };
|
112 | this.subchannelStateListener = (subchannel, previousState, newState) => {
|
113 | this.subchannelStateCounts[previousState] -= 1;
|
114 | this.subchannelStateCounts[newState] += 1;
|
115 | |
116 |
|
117 |
|
118 |
|
119 | if (subchannel === this.subchannels[this.currentSubchannelIndex] &&
|
120 | newState === connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) {
|
121 | this.startNextSubchannelConnecting();
|
122 | }
|
123 | if (newState === connectivity_state_1.ConnectivityState.READY) {
|
124 | this.pickSubchannel(subchannel);
|
125 | return;
|
126 | }
|
127 | else {
|
128 | if (this.triedAllSubchannels &&
|
129 | this.subchannelStateCounts[connectivity_state_1.ConnectivityState.IDLE] ===
|
130 | this.subchannels.length) {
|
131 | |
132 |
|
133 |
|
134 |
|
135 |
|
136 | this.resetSubchannelList(false);
|
137 | this.updateState(connectivity_state_1.ConnectivityState.IDLE, new picker_1.QueuePicker(this));
|
138 | return;
|
139 | }
|
140 | if (this.currentPick === null) {
|
141 | if (this.triedAllSubchannels) {
|
142 | let newLBState;
|
143 | if (this.subchannelStateCounts[connectivity_state_1.ConnectivityState.CONNECTING] > 0) {
|
144 | newLBState = connectivity_state_1.ConnectivityState.CONNECTING;
|
145 | }
|
146 | else if (this.subchannelStateCounts[connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE] >
|
147 | 0) {
|
148 | newLBState = connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE;
|
149 | }
|
150 | else {
|
151 | newLBState = connectivity_state_1.ConnectivityState.IDLE;
|
152 | }
|
153 | if (newLBState !== this.currentState) {
|
154 | if (newLBState === connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) {
|
155 | this.updateState(newLBState, new picker_1.UnavailablePicker());
|
156 | }
|
157 | else {
|
158 | this.updateState(newLBState, new picker_1.QueuePicker(this));
|
159 | }
|
160 | }
|
161 | }
|
162 | else {
|
163 | this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this));
|
164 | }
|
165 | }
|
166 | }
|
167 | };
|
168 | this.pickedSubchannelStateListener = (subchannel, previousState, newState) => {
|
169 | if (newState !== connectivity_state_1.ConnectivityState.READY) {
|
170 | this.currentPick = null;
|
171 | subchannel.unref();
|
172 | subchannel.removeConnectivityStateListener(this.pickedSubchannelStateListener);
|
173 | this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
|
174 | if (this.subchannels.length > 0) {
|
175 | if (this.triedAllSubchannels) {
|
176 | let newLBState;
|
177 | if (this.subchannelStateCounts[connectivity_state_1.ConnectivityState.CONNECTING] > 0) {
|
178 | newLBState = connectivity_state_1.ConnectivityState.CONNECTING;
|
179 | }
|
180 | else if (this.subchannelStateCounts[connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE] >
|
181 | 0) {
|
182 | newLBState = connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE;
|
183 | }
|
184 | else {
|
185 | newLBState = connectivity_state_1.ConnectivityState.IDLE;
|
186 | }
|
187 | if (newLBState === connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) {
|
188 | this.updateState(newLBState, new picker_1.UnavailablePicker());
|
189 | }
|
190 | else {
|
191 | this.updateState(newLBState, new picker_1.QueuePicker(this));
|
192 | }
|
193 | }
|
194 | else {
|
195 | this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this));
|
196 | }
|
197 | }
|
198 | else {
|
199 | |
200 |
|
201 |
|
202 |
|
203 | this.updateState(connectivity_state_1.ConnectivityState.IDLE, new picker_1.QueuePicker(this));
|
204 | }
|
205 | }
|
206 | };
|
207 | this.connectionDelayTimeout = setTimeout(() => { }, 0);
|
208 | clearTimeout(this.connectionDelayTimeout);
|
209 | }
|
210 | startNextSubchannelConnecting() {
|
211 | if (this.triedAllSubchannels) {
|
212 | return;
|
213 | }
|
214 | for (const [index, subchannel] of this.subchannels.entries()) {
|
215 | if (index > this.currentSubchannelIndex) {
|
216 | const subchannelState = subchannel.getConnectivityState();
|
217 | if (subchannelState === connectivity_state_1.ConnectivityState.IDLE ||
|
218 | subchannelState === connectivity_state_1.ConnectivityState.CONNECTING) {
|
219 | this.startConnecting(index);
|
220 | return;
|
221 | }
|
222 | }
|
223 | }
|
224 | this.triedAllSubchannels = true;
|
225 | }
|
226 | |
227 |
|
228 |
|
229 |
|
230 | startConnecting(subchannelIndex) {
|
231 | clearTimeout(this.connectionDelayTimeout);
|
232 | this.currentSubchannelIndex = subchannelIndex;
|
233 | if (this.subchannels[subchannelIndex].getConnectivityState() ===
|
234 | connectivity_state_1.ConnectivityState.IDLE) {
|
235 | trace('Start connecting to subchannel with address ' +
|
236 | this.subchannels[subchannelIndex].getAddress());
|
237 | process.nextTick(() => {
|
238 | this.subchannels[subchannelIndex].startConnecting();
|
239 | });
|
240 | }
|
241 | this.connectionDelayTimeout = setTimeout(() => {
|
242 | this.startNextSubchannelConnecting();
|
243 | }, CONNECTION_DELAY_INTERVAL_MS);
|
244 | }
|
245 | pickSubchannel(subchannel) {
|
246 | trace('Pick subchannel with address ' + subchannel.getAddress());
|
247 | if (this.currentPick !== null) {
|
248 | this.currentPick.unref();
|
249 | this.currentPick.removeConnectivityStateListener(this.pickedSubchannelStateListener);
|
250 | }
|
251 | this.currentPick = subchannel;
|
252 | this.updateState(connectivity_state_1.ConnectivityState.READY, new PickFirstPicker(subchannel));
|
253 | subchannel.addConnectivityStateListener(this.pickedSubchannelStateListener);
|
254 | subchannel.ref();
|
255 | this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
|
256 | this.resetSubchannelList();
|
257 | clearTimeout(this.connectionDelayTimeout);
|
258 | }
|
259 | updateState(newState, picker) {
|
260 | trace(connectivity_state_1.ConnectivityState[this.currentState] +
|
261 | ' -> ' +
|
262 | connectivity_state_1.ConnectivityState[newState]);
|
263 | this.currentState = newState;
|
264 | this.channelControlHelper.updateState(newState, picker);
|
265 | }
|
266 | resetSubchannelList(resetTriedAllSubchannels = true) {
|
267 | for (const subchannel of this.subchannels) {
|
268 | subchannel.removeConnectivityStateListener(this.subchannelStateListener);
|
269 | subchannel.unref();
|
270 | this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
|
271 | }
|
272 | this.currentSubchannelIndex = 0;
|
273 | this.subchannelStateCounts = {
|
274 | [connectivity_state_1.ConnectivityState.CONNECTING]: 0,
|
275 | [connectivity_state_1.ConnectivityState.IDLE]: 0,
|
276 | [connectivity_state_1.ConnectivityState.READY]: 0,
|
277 | [connectivity_state_1.ConnectivityState.SHUTDOWN]: 0,
|
278 | [connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE]: 0,
|
279 | };
|
280 | this.subchannels = [];
|
281 | if (resetTriedAllSubchannels) {
|
282 | this.triedAllSubchannels = false;
|
283 | }
|
284 | }
|
285 | |
286 |
|
287 |
|
288 |
|
289 | connectToAddressList() {
|
290 | this.resetSubchannelList();
|
291 | trace('Connect to address list ' +
|
292 | this.latestAddressList.map((address) => subchannel_address_1.subchannelAddressToString(address)));
|
293 | this.subchannels = this.latestAddressList.map((address) => this.channelControlHelper.createSubchannel(address, {}));
|
294 | for (const subchannel of this.subchannels) {
|
295 | subchannel.ref();
|
296 | this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
|
297 | }
|
298 | for (const subchannel of this.subchannels) {
|
299 | subchannel.addConnectivityStateListener(this.subchannelStateListener);
|
300 | this.subchannelStateCounts[subchannel.getConnectivityState()] += 1;
|
301 | if (subchannel.getConnectivityState() === connectivity_state_1.ConnectivityState.READY) {
|
302 | this.pickSubchannel(subchannel);
|
303 | this.resetSubchannelList();
|
304 | return;
|
305 | }
|
306 | }
|
307 | for (const [index, subchannel] of this.subchannels.entries()) {
|
308 | const subchannelState = subchannel.getConnectivityState();
|
309 | if (subchannelState === connectivity_state_1.ConnectivityState.IDLE ||
|
310 | subchannelState === connectivity_state_1.ConnectivityState.CONNECTING) {
|
311 | this.startConnecting(index);
|
312 | if (this.currentPick === null) {
|
313 | this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this));
|
314 | }
|
315 | return;
|
316 | }
|
317 | }
|
318 |
|
319 | if (this.currentPick === null) {
|
320 | this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker());
|
321 | }
|
322 | }
|
323 | updateAddressList(addressList, lbConfig) {
|
324 |
|
325 | |
326 |
|
327 |
|
328 | if (this.subchannels.length === 0 ||
|
329 | !this.latestAddressList.every((value, index) => addressList[index] === value)) {
|
330 | this.latestAddressList = addressList;
|
331 | this.connectToAddressList();
|
332 | }
|
333 | }
|
334 | exitIdle() {
|
335 | if (this.currentState === connectivity_state_1.ConnectivityState.IDLE ||
|
336 | this.triedAllSubchannels) {
|
337 | this.channelControlHelper.requestReresolution();
|
338 | }
|
339 | for (const subchannel of this.subchannels) {
|
340 | subchannel.startConnecting();
|
341 | }
|
342 | if (this.currentState === connectivity_state_1.ConnectivityState.IDLE) {
|
343 | if (this.latestAddressList.length > 0) {
|
344 | this.connectToAddressList();
|
345 | }
|
346 | }
|
347 | }
|
348 | resetBackoff() {
|
349 | |
350 |
|
351 | }
|
352 | destroy() {
|
353 | this.resetSubchannelList();
|
354 | if (this.currentPick !== null) {
|
355 | |
356 |
|
357 |
|
358 | const currentPick = this.currentPick;
|
359 | currentPick.unref();
|
360 | currentPick.removeConnectivityStateListener(this.pickedSubchannelStateListener);
|
361 | this.channelControlHelper.removeChannelzChild(currentPick.getChannelzRef());
|
362 | }
|
363 | }
|
364 | getTypeName() {
|
365 | return TYPE_NAME;
|
366 | }
|
367 | }
|
368 | exports.PickFirstLoadBalancer = PickFirstLoadBalancer;
|
369 | function setup() {
|
370 | load_balancer_1.registerLoadBalancerType(TYPE_NAME, PickFirstLoadBalancer, PickFirstLoadBalancingConfig);
|
371 | load_balancer_1.registerDefaultLoadBalancerType(TYPE_NAME);
|
372 | }
|
373 | exports.setup = setup;
|
374 |
|
\ | No newline at end of file |