mirror of
https://github.com/tvytlx/ai-agent-deep-dive.git
synced 2026-04-09 10:34:48 +08:00
Add extracted source directory and README navigation
This commit is contained in:
571
extracted-source/node_modules/@grpc/grpc-js/build/src/load-balancer-outlier-detection.js
generated
vendored
Normal file
571
extracted-source/node_modules/@grpc/grpc-js/build/src/load-balancer-outlier-detection.js
generated
vendored
Normal file
@@ -0,0 +1,571 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Copyright 2022 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.OutlierDetectionLoadBalancer = exports.OutlierDetectionLoadBalancingConfig = void 0;
|
||||
exports.setup = setup;
|
||||
const connectivity_state_1 = require("./connectivity-state");
|
||||
const constants_1 = require("./constants");
|
||||
const duration_1 = require("./duration");
|
||||
const experimental_1 = require("./experimental");
|
||||
const load_balancer_1 = require("./load-balancer");
|
||||
const load_balancer_child_handler_1 = require("./load-balancer-child-handler");
|
||||
const picker_1 = require("./picker");
|
||||
const subchannel_address_1 = require("./subchannel-address");
|
||||
const subchannel_interface_1 = require("./subchannel-interface");
|
||||
const logging = require("./logging");
|
||||
const TRACER_NAME = 'outlier_detection';
|
||||
function trace(text) {
|
||||
logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, text);
|
||||
}
|
||||
const TYPE_NAME = 'outlier_detection';
|
||||
const OUTLIER_DETECTION_ENABLED = ((_a = process.env.GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION) !== null && _a !== void 0 ? _a : 'true') === 'true';
|
||||
const defaultSuccessRateEjectionConfig = {
|
||||
stdev_factor: 1900,
|
||||
enforcement_percentage: 100,
|
||||
minimum_hosts: 5,
|
||||
request_volume: 100,
|
||||
};
|
||||
const defaultFailurePercentageEjectionConfig = {
|
||||
threshold: 85,
|
||||
enforcement_percentage: 100,
|
||||
minimum_hosts: 5,
|
||||
request_volume: 50,
|
||||
};
|
||||
function validateFieldType(obj, fieldName, expectedType, objectName) {
|
||||
if (fieldName in obj &&
|
||||
obj[fieldName] !== undefined &&
|
||||
typeof obj[fieldName] !== expectedType) {
|
||||
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
|
||||
throw new Error(`outlier detection config ${fullFieldName} parse error: expected ${expectedType}, got ${typeof obj[fieldName]}`);
|
||||
}
|
||||
}
|
||||
function validatePositiveDuration(obj, fieldName, objectName) {
|
||||
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
|
||||
if (fieldName in obj && obj[fieldName] !== undefined) {
|
||||
if (!(0, duration_1.isDuration)(obj[fieldName])) {
|
||||
throw new Error(`outlier detection config ${fullFieldName} parse error: expected Duration, got ${typeof obj[fieldName]}`);
|
||||
}
|
||||
if (!(obj[fieldName].seconds >= 0 &&
|
||||
obj[fieldName].seconds <= 315576000000 &&
|
||||
obj[fieldName].nanos >= 0 &&
|
||||
obj[fieldName].nanos <= 999999999)) {
|
||||
throw new Error(`outlier detection config ${fullFieldName} parse error: values out of range for non-negative Duaration`);
|
||||
}
|
||||
}
|
||||
}
|
||||
function validatePercentage(obj, fieldName, objectName) {
|
||||
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
|
||||
validateFieldType(obj, fieldName, 'number', objectName);
|
||||
if (fieldName in obj &&
|
||||
obj[fieldName] !== undefined &&
|
||||
!(obj[fieldName] >= 0 && obj[fieldName] <= 100)) {
|
||||
throw new Error(`outlier detection config ${fullFieldName} parse error: value out of range for percentage (0-100)`);
|
||||
}
|
||||
}
|
||||
class OutlierDetectionLoadBalancingConfig {
|
||||
constructor(intervalMs, baseEjectionTimeMs, maxEjectionTimeMs, maxEjectionPercent, successRateEjection, failurePercentageEjection, childPolicy) {
|
||||
this.childPolicy = childPolicy;
|
||||
if (childPolicy.getLoadBalancerName() === 'pick_first') {
|
||||
throw new Error('outlier_detection LB policy cannot have a pick_first child policy');
|
||||
}
|
||||
this.intervalMs = intervalMs !== null && intervalMs !== void 0 ? intervalMs : 10000;
|
||||
this.baseEjectionTimeMs = baseEjectionTimeMs !== null && baseEjectionTimeMs !== void 0 ? baseEjectionTimeMs : 30000;
|
||||
this.maxEjectionTimeMs = maxEjectionTimeMs !== null && maxEjectionTimeMs !== void 0 ? maxEjectionTimeMs : 300000;
|
||||
this.maxEjectionPercent = maxEjectionPercent !== null && maxEjectionPercent !== void 0 ? maxEjectionPercent : 10;
|
||||
this.successRateEjection = successRateEjection
|
||||
? Object.assign(Object.assign({}, defaultSuccessRateEjectionConfig), successRateEjection) : null;
|
||||
this.failurePercentageEjection = failurePercentageEjection
|
||||
? Object.assign(Object.assign({}, defaultFailurePercentageEjectionConfig), failurePercentageEjection) : null;
|
||||
}
|
||||
getLoadBalancerName() {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
toJsonObject() {
|
||||
var _a, _b;
|
||||
return {
|
||||
outlier_detection: {
|
||||
interval: (0, duration_1.msToDuration)(this.intervalMs),
|
||||
base_ejection_time: (0, duration_1.msToDuration)(this.baseEjectionTimeMs),
|
||||
max_ejection_time: (0, duration_1.msToDuration)(this.maxEjectionTimeMs),
|
||||
max_ejection_percent: this.maxEjectionPercent,
|
||||
success_rate_ejection: (_a = this.successRateEjection) !== null && _a !== void 0 ? _a : undefined,
|
||||
failure_percentage_ejection: (_b = this.failurePercentageEjection) !== null && _b !== void 0 ? _b : undefined,
|
||||
child_policy: [this.childPolicy.toJsonObject()],
|
||||
},
|
||||
};
|
||||
}
|
||||
getIntervalMs() {
|
||||
return this.intervalMs;
|
||||
}
|
||||
getBaseEjectionTimeMs() {
|
||||
return this.baseEjectionTimeMs;
|
||||
}
|
||||
getMaxEjectionTimeMs() {
|
||||
return this.maxEjectionTimeMs;
|
||||
}
|
||||
getMaxEjectionPercent() {
|
||||
return this.maxEjectionPercent;
|
||||
}
|
||||
getSuccessRateEjectionConfig() {
|
||||
return this.successRateEjection;
|
||||
}
|
||||
getFailurePercentageEjectionConfig() {
|
||||
return this.failurePercentageEjection;
|
||||
}
|
||||
getChildPolicy() {
|
||||
return this.childPolicy;
|
||||
}
|
||||
static createFromJson(obj) {
|
||||
var _a;
|
||||
validatePositiveDuration(obj, 'interval');
|
||||
validatePositiveDuration(obj, 'base_ejection_time');
|
||||
validatePositiveDuration(obj, 'max_ejection_time');
|
||||
validatePercentage(obj, 'max_ejection_percent');
|
||||
if ('success_rate_ejection' in obj &&
|
||||
obj.success_rate_ejection !== undefined) {
|
||||
if (typeof obj.success_rate_ejection !== 'object') {
|
||||
throw new Error('outlier detection config success_rate_ejection must be an object');
|
||||
}
|
||||
validateFieldType(obj.success_rate_ejection, 'stdev_factor', 'number', 'success_rate_ejection');
|
||||
validatePercentage(obj.success_rate_ejection, 'enforcement_percentage', 'success_rate_ejection');
|
||||
validateFieldType(obj.success_rate_ejection, 'minimum_hosts', 'number', 'success_rate_ejection');
|
||||
validateFieldType(obj.success_rate_ejection, 'request_volume', 'number', 'success_rate_ejection');
|
||||
}
|
||||
if ('failure_percentage_ejection' in obj &&
|
||||
obj.failure_percentage_ejection !== undefined) {
|
||||
if (typeof obj.failure_percentage_ejection !== 'object') {
|
||||
throw new Error('outlier detection config failure_percentage_ejection must be an object');
|
||||
}
|
||||
validatePercentage(obj.failure_percentage_ejection, 'threshold', 'failure_percentage_ejection');
|
||||
validatePercentage(obj.failure_percentage_ejection, 'enforcement_percentage', 'failure_percentage_ejection');
|
||||
validateFieldType(obj.failure_percentage_ejection, 'minimum_hosts', 'number', 'failure_percentage_ejection');
|
||||
validateFieldType(obj.failure_percentage_ejection, 'request_volume', 'number', 'failure_percentage_ejection');
|
||||
}
|
||||
if (!('child_policy' in obj) || !Array.isArray(obj.child_policy)) {
|
||||
throw new Error('outlier detection config child_policy must be an array');
|
||||
}
|
||||
const childPolicy = (0, load_balancer_1.selectLbConfigFromList)(obj.child_policy);
|
||||
if (!childPolicy) {
|
||||
throw new Error('outlier detection config child_policy: no valid recognized policy found');
|
||||
}
|
||||
return new OutlierDetectionLoadBalancingConfig(obj.interval ? (0, duration_1.durationToMs)(obj.interval) : null, obj.base_ejection_time ? (0, duration_1.durationToMs)(obj.base_ejection_time) : null, obj.max_ejection_time ? (0, duration_1.durationToMs)(obj.max_ejection_time) : null, (_a = obj.max_ejection_percent) !== null && _a !== void 0 ? _a : null, obj.success_rate_ejection, obj.failure_percentage_ejection, childPolicy);
|
||||
}
|
||||
}
|
||||
exports.OutlierDetectionLoadBalancingConfig = OutlierDetectionLoadBalancingConfig;
|
||||
class OutlierDetectionSubchannelWrapper extends subchannel_interface_1.BaseSubchannelWrapper {
|
||||
constructor(childSubchannel, mapEntry) {
|
||||
super(childSubchannel);
|
||||
this.mapEntry = mapEntry;
|
||||
this.refCount = 0;
|
||||
}
|
||||
ref() {
|
||||
this.child.ref();
|
||||
this.refCount += 1;
|
||||
}
|
||||
unref() {
|
||||
this.child.unref();
|
||||
this.refCount -= 1;
|
||||
if (this.refCount <= 0) {
|
||||
if (this.mapEntry) {
|
||||
const index = this.mapEntry.subchannelWrappers.indexOf(this);
|
||||
if (index >= 0) {
|
||||
this.mapEntry.subchannelWrappers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eject() {
|
||||
this.setHealthy(false);
|
||||
}
|
||||
uneject() {
|
||||
this.setHealthy(true);
|
||||
}
|
||||
getMapEntry() {
|
||||
return this.mapEntry;
|
||||
}
|
||||
getWrappedSubchannel() {
|
||||
return this.child;
|
||||
}
|
||||
}
|
||||
function createEmptyBucket() {
|
||||
return {
|
||||
success: 0,
|
||||
failure: 0,
|
||||
};
|
||||
}
|
||||
class CallCounter {
|
||||
constructor() {
|
||||
this.activeBucket = createEmptyBucket();
|
||||
this.inactiveBucket = createEmptyBucket();
|
||||
}
|
||||
addSuccess() {
|
||||
this.activeBucket.success += 1;
|
||||
}
|
||||
addFailure() {
|
||||
this.activeBucket.failure += 1;
|
||||
}
|
||||
switchBuckets() {
|
||||
this.inactiveBucket = this.activeBucket;
|
||||
this.activeBucket = createEmptyBucket();
|
||||
}
|
||||
getLastSuccesses() {
|
||||
return this.inactiveBucket.success;
|
||||
}
|
||||
getLastFailures() {
|
||||
return this.inactiveBucket.failure;
|
||||
}
|
||||
}
|
||||
class OutlierDetectionPicker {
|
||||
constructor(wrappedPicker, countCalls) {
|
||||
this.wrappedPicker = wrappedPicker;
|
||||
this.countCalls = countCalls;
|
||||
}
|
||||
pick(pickArgs) {
|
||||
const wrappedPick = this.wrappedPicker.pick(pickArgs);
|
||||
if (wrappedPick.pickResultType === picker_1.PickResultType.COMPLETE) {
|
||||
const subchannelWrapper = wrappedPick.subchannel;
|
||||
const mapEntry = subchannelWrapper.getMapEntry();
|
||||
if (mapEntry) {
|
||||
let onCallEnded = wrappedPick.onCallEnded;
|
||||
if (this.countCalls) {
|
||||
onCallEnded = (statusCode, details, metadata) => {
|
||||
var _a;
|
||||
if (statusCode === constants_1.Status.OK) {
|
||||
mapEntry.counter.addSuccess();
|
||||
}
|
||||
else {
|
||||
mapEntry.counter.addFailure();
|
||||
}
|
||||
(_a = wrappedPick.onCallEnded) === null || _a === void 0 ? void 0 : _a.call(wrappedPick, statusCode, details, metadata);
|
||||
};
|
||||
}
|
||||
return Object.assign(Object.assign({}, wrappedPick), { subchannel: subchannelWrapper.getWrappedSubchannel(), onCallEnded: onCallEnded });
|
||||
}
|
||||
else {
|
||||
return Object.assign(Object.assign({}, wrappedPick), { subchannel: subchannelWrapper.getWrappedSubchannel() });
|
||||
}
|
||||
}
|
||||
else {
|
||||
return wrappedPick;
|
||||
}
|
||||
}
|
||||
}
|
||||
class OutlierDetectionLoadBalancer {
|
||||
constructor(channelControlHelper) {
|
||||
this.entryMap = new subchannel_address_1.EndpointMap();
|
||||
this.latestConfig = null;
|
||||
this.timerStartTime = null;
|
||||
this.childBalancer = new load_balancer_child_handler_1.ChildLoadBalancerHandler((0, experimental_1.createChildChannelControlHelper)(channelControlHelper, {
|
||||
createSubchannel: (subchannelAddress, subchannelArgs) => {
|
||||
const originalSubchannel = channelControlHelper.createSubchannel(subchannelAddress, subchannelArgs);
|
||||
const mapEntry = this.entryMap.getForSubchannelAddress(subchannelAddress);
|
||||
const subchannelWrapper = new OutlierDetectionSubchannelWrapper(originalSubchannel, mapEntry);
|
||||
if ((mapEntry === null || mapEntry === void 0 ? void 0 : mapEntry.currentEjectionTimestamp) !== null) {
|
||||
// If the address is ejected, propagate that to the new subchannel wrapper
|
||||
subchannelWrapper.eject();
|
||||
}
|
||||
mapEntry === null || mapEntry === void 0 ? void 0 : mapEntry.subchannelWrappers.push(subchannelWrapper);
|
||||
return subchannelWrapper;
|
||||
},
|
||||
updateState: (connectivityState, picker, errorMessage) => {
|
||||
if (connectivityState === connectivity_state_1.ConnectivityState.READY) {
|
||||
channelControlHelper.updateState(connectivityState, new OutlierDetectionPicker(picker, this.isCountingEnabled()), errorMessage);
|
||||
}
|
||||
else {
|
||||
channelControlHelper.updateState(connectivityState, picker, errorMessage);
|
||||
}
|
||||
},
|
||||
}));
|
||||
this.ejectionTimer = setInterval(() => { }, 0);
|
||||
clearInterval(this.ejectionTimer);
|
||||
}
|
||||
isCountingEnabled() {
|
||||
return (this.latestConfig !== null &&
|
||||
(this.latestConfig.getSuccessRateEjectionConfig() !== null ||
|
||||
this.latestConfig.getFailurePercentageEjectionConfig() !== null));
|
||||
}
|
||||
getCurrentEjectionPercent() {
|
||||
let ejectionCount = 0;
|
||||
for (const mapEntry of this.entryMap.values()) {
|
||||
if (mapEntry.currentEjectionTimestamp !== null) {
|
||||
ejectionCount += 1;
|
||||
}
|
||||
}
|
||||
return (ejectionCount * 100) / this.entryMap.size;
|
||||
}
|
||||
runSuccessRateCheck(ejectionTimestamp) {
|
||||
if (!this.latestConfig) {
|
||||
return;
|
||||
}
|
||||
const successRateConfig = this.latestConfig.getSuccessRateEjectionConfig();
|
||||
if (!successRateConfig) {
|
||||
return;
|
||||
}
|
||||
trace('Running success rate check');
|
||||
// Step 1
|
||||
const targetRequestVolume = successRateConfig.request_volume;
|
||||
let addresesWithTargetVolume = 0;
|
||||
const successRates = [];
|
||||
for (const [endpoint, mapEntry] of this.entryMap.entries()) {
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
trace('Stats for ' +
|
||||
(0, subchannel_address_1.endpointToString)(endpoint) +
|
||||
': successes=' +
|
||||
successes +
|
||||
' failures=' +
|
||||
failures +
|
||||
' targetRequestVolume=' +
|
||||
targetRequestVolume);
|
||||
if (successes + failures >= targetRequestVolume) {
|
||||
addresesWithTargetVolume += 1;
|
||||
successRates.push(successes / (successes + failures));
|
||||
}
|
||||
}
|
||||
trace('Found ' +
|
||||
addresesWithTargetVolume +
|
||||
' success rate candidates; currentEjectionPercent=' +
|
||||
this.getCurrentEjectionPercent() +
|
||||
' successRates=[' +
|
||||
successRates +
|
||||
']');
|
||||
if (addresesWithTargetVolume < successRateConfig.minimum_hosts) {
|
||||
return;
|
||||
}
|
||||
// Step 2
|
||||
const successRateMean = successRates.reduce((a, b) => a + b) / successRates.length;
|
||||
let successRateDeviationSum = 0;
|
||||
for (const rate of successRates) {
|
||||
const deviation = rate - successRateMean;
|
||||
successRateDeviationSum += deviation * deviation;
|
||||
}
|
||||
const successRateVariance = successRateDeviationSum / successRates.length;
|
||||
const successRateStdev = Math.sqrt(successRateVariance);
|
||||
const ejectionThreshold = successRateMean -
|
||||
successRateStdev * (successRateConfig.stdev_factor / 1000);
|
||||
trace('stdev=' + successRateStdev + ' ejectionThreshold=' + ejectionThreshold);
|
||||
// Step 3
|
||||
for (const [address, mapEntry] of this.entryMap.entries()) {
|
||||
// Step 3.i
|
||||
if (this.getCurrentEjectionPercent() >=
|
||||
this.latestConfig.getMaxEjectionPercent()) {
|
||||
break;
|
||||
}
|
||||
// Step 3.ii
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
if (successes + failures < targetRequestVolume) {
|
||||
continue;
|
||||
}
|
||||
// Step 3.iii
|
||||
const successRate = successes / (successes + failures);
|
||||
trace('Checking candidate ' + address + ' successRate=' + successRate);
|
||||
if (successRate < ejectionThreshold) {
|
||||
const randomNumber = Math.random() * 100;
|
||||
trace('Candidate ' +
|
||||
address +
|
||||
' randomNumber=' +
|
||||
randomNumber +
|
||||
' enforcement_percentage=' +
|
||||
successRateConfig.enforcement_percentage);
|
||||
if (randomNumber < successRateConfig.enforcement_percentage) {
|
||||
trace('Ejecting candidate ' + address);
|
||||
this.eject(mapEntry, ejectionTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
runFailurePercentageCheck(ejectionTimestamp) {
|
||||
if (!this.latestConfig) {
|
||||
return;
|
||||
}
|
||||
const failurePercentageConfig = this.latestConfig.getFailurePercentageEjectionConfig();
|
||||
if (!failurePercentageConfig) {
|
||||
return;
|
||||
}
|
||||
trace('Running failure percentage check. threshold=' +
|
||||
failurePercentageConfig.threshold +
|
||||
' request volume threshold=' +
|
||||
failurePercentageConfig.request_volume);
|
||||
// Step 1
|
||||
let addressesWithTargetVolume = 0;
|
||||
for (const mapEntry of this.entryMap.values()) {
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
if (successes + failures >= failurePercentageConfig.request_volume) {
|
||||
addressesWithTargetVolume += 1;
|
||||
}
|
||||
}
|
||||
if (addressesWithTargetVolume < failurePercentageConfig.minimum_hosts) {
|
||||
return;
|
||||
}
|
||||
// Step 2
|
||||
for (const [address, mapEntry] of this.entryMap.entries()) {
|
||||
// Step 2.i
|
||||
if (this.getCurrentEjectionPercent() >=
|
||||
this.latestConfig.getMaxEjectionPercent()) {
|
||||
break;
|
||||
}
|
||||
// Step 2.ii
|
||||
const successes = mapEntry.counter.getLastSuccesses();
|
||||
const failures = mapEntry.counter.getLastFailures();
|
||||
trace('Candidate successes=' + successes + ' failures=' + failures);
|
||||
if (successes + failures < failurePercentageConfig.request_volume) {
|
||||
continue;
|
||||
}
|
||||
// Step 2.iii
|
||||
const failurePercentage = (failures * 100) / (failures + successes);
|
||||
if (failurePercentage > failurePercentageConfig.threshold) {
|
||||
const randomNumber = Math.random() * 100;
|
||||
trace('Candidate ' +
|
||||
address +
|
||||
' randomNumber=' +
|
||||
randomNumber +
|
||||
' enforcement_percentage=' +
|
||||
failurePercentageConfig.enforcement_percentage);
|
||||
if (randomNumber < failurePercentageConfig.enforcement_percentage) {
|
||||
trace('Ejecting candidate ' + address);
|
||||
this.eject(mapEntry, ejectionTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eject(mapEntry, ejectionTimestamp) {
|
||||
mapEntry.currentEjectionTimestamp = new Date();
|
||||
mapEntry.ejectionTimeMultiplier += 1;
|
||||
for (const subchannelWrapper of mapEntry.subchannelWrappers) {
|
||||
subchannelWrapper.eject();
|
||||
}
|
||||
}
|
||||
uneject(mapEntry) {
|
||||
mapEntry.currentEjectionTimestamp = null;
|
||||
for (const subchannelWrapper of mapEntry.subchannelWrappers) {
|
||||
subchannelWrapper.uneject();
|
||||
}
|
||||
}
|
||||
switchAllBuckets() {
|
||||
for (const mapEntry of this.entryMap.values()) {
|
||||
mapEntry.counter.switchBuckets();
|
||||
}
|
||||
}
|
||||
startTimer(delayMs) {
|
||||
var _a, _b;
|
||||
this.ejectionTimer = setTimeout(() => this.runChecks(), delayMs);
|
||||
(_b = (_a = this.ejectionTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
||||
}
|
||||
runChecks() {
|
||||
const ejectionTimestamp = new Date();
|
||||
trace('Ejection timer running');
|
||||
this.switchAllBuckets();
|
||||
if (!this.latestConfig) {
|
||||
return;
|
||||
}
|
||||
this.timerStartTime = ejectionTimestamp;
|
||||
this.startTimer(this.latestConfig.getIntervalMs());
|
||||
this.runSuccessRateCheck(ejectionTimestamp);
|
||||
this.runFailurePercentageCheck(ejectionTimestamp);
|
||||
for (const [address, mapEntry] of this.entryMap.entries()) {
|
||||
if (mapEntry.currentEjectionTimestamp === null) {
|
||||
if (mapEntry.ejectionTimeMultiplier > 0) {
|
||||
mapEntry.ejectionTimeMultiplier -= 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const baseEjectionTimeMs = this.latestConfig.getBaseEjectionTimeMs();
|
||||
const maxEjectionTimeMs = this.latestConfig.getMaxEjectionTimeMs();
|
||||
const returnTime = new Date(mapEntry.currentEjectionTimestamp.getTime());
|
||||
returnTime.setMilliseconds(returnTime.getMilliseconds() +
|
||||
Math.min(baseEjectionTimeMs * mapEntry.ejectionTimeMultiplier, Math.max(baseEjectionTimeMs, maxEjectionTimeMs)));
|
||||
if (returnTime < new Date()) {
|
||||
trace('Unejecting ' + address);
|
||||
this.uneject(mapEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateAddressList(endpointList, lbConfig, options, resolutionNote) {
|
||||
if (!(lbConfig instanceof OutlierDetectionLoadBalancingConfig)) {
|
||||
return false;
|
||||
}
|
||||
trace('Received update with config: ' + JSON.stringify(lbConfig.toJsonObject(), undefined, 2));
|
||||
if (endpointList.ok) {
|
||||
for (const endpoint of endpointList.value) {
|
||||
if (!this.entryMap.has(endpoint)) {
|
||||
trace('Adding map entry for ' + (0, subchannel_address_1.endpointToString)(endpoint));
|
||||
this.entryMap.set(endpoint, {
|
||||
counter: new CallCounter(),
|
||||
currentEjectionTimestamp: null,
|
||||
ejectionTimeMultiplier: 0,
|
||||
subchannelWrappers: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
this.entryMap.deleteMissing(endpointList.value);
|
||||
}
|
||||
const childPolicy = lbConfig.getChildPolicy();
|
||||
this.childBalancer.updateAddressList(endpointList, childPolicy, options, resolutionNote);
|
||||
if (lbConfig.getSuccessRateEjectionConfig() ||
|
||||
lbConfig.getFailurePercentageEjectionConfig()) {
|
||||
if (this.timerStartTime) {
|
||||
trace('Previous timer existed. Replacing timer');
|
||||
clearTimeout(this.ejectionTimer);
|
||||
const remainingDelay = lbConfig.getIntervalMs() -
|
||||
(new Date().getTime() - this.timerStartTime.getTime());
|
||||
this.startTimer(remainingDelay);
|
||||
}
|
||||
else {
|
||||
trace('Starting new timer');
|
||||
this.timerStartTime = new Date();
|
||||
this.startTimer(lbConfig.getIntervalMs());
|
||||
this.switchAllBuckets();
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace('Counting disabled. Cancelling timer.');
|
||||
this.timerStartTime = null;
|
||||
clearTimeout(this.ejectionTimer);
|
||||
for (const mapEntry of this.entryMap.values()) {
|
||||
this.uneject(mapEntry);
|
||||
mapEntry.ejectionTimeMultiplier = 0;
|
||||
}
|
||||
}
|
||||
this.latestConfig = lbConfig;
|
||||
return true;
|
||||
}
|
||||
exitIdle() {
|
||||
this.childBalancer.exitIdle();
|
||||
}
|
||||
resetBackoff() {
|
||||
this.childBalancer.resetBackoff();
|
||||
}
|
||||
destroy() {
|
||||
clearTimeout(this.ejectionTimer);
|
||||
this.childBalancer.destroy();
|
||||
}
|
||||
getTypeName() {
|
||||
return TYPE_NAME;
|
||||
}
|
||||
}
|
||||
exports.OutlierDetectionLoadBalancer = OutlierDetectionLoadBalancer;
|
||||
function setup() {
|
||||
if (OUTLIER_DETECTION_ENABLED) {
|
||||
(0, experimental_1.registerLoadBalancerType)(TYPE_NAME, OutlierDetectionLoadBalancer, OutlierDetectionLoadBalancingConfig);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=load-balancer-outlier-detection.js.map
|
||||
Reference in New Issue
Block a user