|
"use strict"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.RoundRobinLoadBalancer = void 0; |
|
exports.setup = setup; |
|
const load_balancer_1 = require("./load-balancer"); |
|
const connectivity_state_1 = require("./connectivity-state"); |
|
const picker_1 = require("./picker"); |
|
const logging = require("./logging"); |
|
const constants_1 = require("./constants"); |
|
const subchannel_address_1 = require("./subchannel-address"); |
|
const load_balancer_pick_first_1 = require("./load-balancer-pick-first"); |
|
const TRACER_NAME = 'round_robin'; |
|
function trace(text) { |
|
logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, text); |
|
} |
|
const TYPE_NAME = 'round_robin'; |
|
class RoundRobinLoadBalancingConfig { |
|
getLoadBalancerName() { |
|
return TYPE_NAME; |
|
} |
|
constructor() { } |
|
toJsonObject() { |
|
return { |
|
[TYPE_NAME]: {}, |
|
}; |
|
} |
|
|
|
static createFromJson(obj) { |
|
return new RoundRobinLoadBalancingConfig(); |
|
} |
|
} |
|
class RoundRobinPicker { |
|
constructor(children, nextIndex = 0) { |
|
this.children = children; |
|
this.nextIndex = nextIndex; |
|
} |
|
pick(pickArgs) { |
|
const childPicker = this.children[this.nextIndex].picker; |
|
this.nextIndex = (this.nextIndex + 1) % this.children.length; |
|
return childPicker.pick(pickArgs); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
peekNextEndpoint() { |
|
return this.children[this.nextIndex].endpoint; |
|
} |
|
} |
|
class RoundRobinLoadBalancer { |
|
constructor(channelControlHelper) { |
|
this.channelControlHelper = channelControlHelper; |
|
this.children = []; |
|
this.currentState = connectivity_state_1.ConnectivityState.IDLE; |
|
this.currentReadyPicker = null; |
|
this.updatesPaused = false; |
|
this.lastError = null; |
|
this.childChannelControlHelper = (0, load_balancer_1.createChildChannelControlHelper)(channelControlHelper, { |
|
updateState: (connectivityState, picker, errorMessage) => { |
|
|
|
|
|
|
|
|
|
if (this.currentState === connectivity_state_1.ConnectivityState.READY && connectivityState !== connectivity_state_1.ConnectivityState.READY) { |
|
this.channelControlHelper.requestReresolution(); |
|
} |
|
if (errorMessage) { |
|
this.lastError = errorMessage; |
|
} |
|
this.calculateAndUpdateState(); |
|
}, |
|
}); |
|
} |
|
countChildrenWithState(state) { |
|
return this.children.filter(child => child.getConnectivityState() === state) |
|
.length; |
|
} |
|
calculateAndUpdateState() { |
|
if (this.updatesPaused) { |
|
return; |
|
} |
|
if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.READY) > 0) { |
|
const readyChildren = this.children.filter(child => child.getConnectivityState() === connectivity_state_1.ConnectivityState.READY); |
|
let index = 0; |
|
if (this.currentReadyPicker !== null) { |
|
const nextPickedEndpoint = this.currentReadyPicker.peekNextEndpoint(); |
|
index = readyChildren.findIndex(child => (0, subchannel_address_1.endpointEqual)(child.getEndpoint(), nextPickedEndpoint)); |
|
if (index < 0) { |
|
index = 0; |
|
} |
|
} |
|
this.updateState(connectivity_state_1.ConnectivityState.READY, new RoundRobinPicker(readyChildren.map(child => ({ |
|
endpoint: child.getEndpoint(), |
|
picker: child.getPicker(), |
|
})), index), null); |
|
} |
|
else if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.CONNECTING) > 0) { |
|
this.updateState(connectivity_state_1.ConnectivityState.CONNECTING, new picker_1.QueuePicker(this), null); |
|
} |
|
else if (this.countChildrenWithState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE) > 0) { |
|
const errorMessage = `round_robin: No connection established. Last error: ${this.lastError}`; |
|
this.updateState(connectivity_state_1.ConnectivityState.TRANSIENT_FAILURE, new picker_1.UnavailablePicker({ |
|
details: errorMessage, |
|
}), errorMessage); |
|
} |
|
else { |
|
this.updateState(connectivity_state_1.ConnectivityState.IDLE, new picker_1.QueuePicker(this), null); |
|
} |
|
|
|
|
|
|
|
|
|
for (const child of this.children) { |
|
if (child.getConnectivityState() === connectivity_state_1.ConnectivityState.IDLE) { |
|
child.exitIdle(); |
|
} |
|
} |
|
} |
|
updateState(newState, picker, errorMessage) { |
|
trace(connectivity_state_1.ConnectivityState[this.currentState] + |
|
' -> ' + |
|
connectivity_state_1.ConnectivityState[newState]); |
|
if (newState === connectivity_state_1.ConnectivityState.READY) { |
|
this.currentReadyPicker = picker; |
|
} |
|
else { |
|
this.currentReadyPicker = null; |
|
} |
|
this.currentState = newState; |
|
this.channelControlHelper.updateState(newState, picker, errorMessage); |
|
} |
|
resetSubchannelList() { |
|
for (const child of this.children) { |
|
child.destroy(); |
|
} |
|
} |
|
updateAddressList(endpointList, lbConfig, options) { |
|
this.resetSubchannelList(); |
|
trace('Connect to endpoint list ' + endpointList.map(subchannel_address_1.endpointToString)); |
|
this.updatesPaused = true; |
|
this.children = endpointList.map(endpoint => new load_balancer_pick_first_1.LeafLoadBalancer(endpoint, this.childChannelControlHelper, options)); |
|
for (const child of this.children) { |
|
child.startConnecting(); |
|
} |
|
this.updatesPaused = false; |
|
this.calculateAndUpdateState(); |
|
} |
|
exitIdle() { |
|
|
|
|
|
|
|
} |
|
resetBackoff() { |
|
|
|
} |
|
destroy() { |
|
this.resetSubchannelList(); |
|
} |
|
getTypeName() { |
|
return TYPE_NAME; |
|
} |
|
} |
|
exports.RoundRobinLoadBalancer = RoundRobinLoadBalancer; |
|
function setup() { |
|
(0, load_balancer_1.registerLoadBalancerType)(TYPE_NAME, RoundRobinLoadBalancer, RoundRobinLoadBalancingConfig); |
|
} |
|
|