grpc-js: Update outlier detection to address recent spec changes

This commit is contained in:
Michael Lumish 2022-04-21 14:42:04 -07:00
parent 879d13b6c3
commit b07ea8b354
1 changed files with 42 additions and 9 deletions

View File

@ -334,17 +334,21 @@ class OutlierDetectionCounterFilterFactory implements FilterFactory<OutlierDetec
} }
class OutlierDetectionPicker implements Picker { class OutlierDetectionPicker implements Picker {
constructor(private wrappedPicker: Picker) {} constructor(private wrappedPicker: Picker, private countCalls: boolean) {}
pick(pickArgs: PickArgs): PickResult { pick(pickArgs: PickArgs): PickResult {
const wrappedPick = this.wrappedPicker.pick(pickArgs); const wrappedPick = this.wrappedPicker.pick(pickArgs);
if (wrappedPick.pickResultType === PickResultType.COMPLETE) { if (wrappedPick.pickResultType === PickResultType.COMPLETE) {
const subchannelWrapper = wrappedPick.subchannel as OutlierDetectionSubchannelWrapper; const subchannelWrapper = wrappedPick.subchannel as OutlierDetectionSubchannelWrapper;
const mapEntry = subchannelWrapper.getMapEntry(); const mapEntry = subchannelWrapper.getMapEntry();
if (mapEntry) { if (mapEntry) {
const extraFilterFactories = [...wrappedPick.extraFilterFactories];
if (this.countCalls) {
extraFilterFactories.push(new OutlierDetectionCounterFilterFactory(mapEntry.counter));
}
return { return {
...wrappedPick, ...wrappedPick,
subchannel: subchannelWrapper.getWrappedSubchannel(), subchannel: subchannelWrapper.getWrappedSubchannel(),
extraFilterFactories: [...wrappedPick.extraFilterFactories, new OutlierDetectionCounterFilterFactory(mapEntry.counter)] extraFilterFactories: extraFilterFactories
}; };
} else { } else {
return wrappedPick; return wrappedPick;
@ -361,6 +365,7 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
private addressMap: Map<string, MapEntry> = new Map<string, MapEntry>(); private addressMap: Map<string, MapEntry> = new Map<string, MapEntry>();
private latestConfig: OutlierDetectionLoadBalancingConfig | null = null; private latestConfig: OutlierDetectionLoadBalancingConfig | null = null;
private ejectionTimer: NodeJS.Timer; private ejectionTimer: NodeJS.Timer;
private timerStartTime: Date | null = null;
constructor(channelControlHelper: ChannelControlHelper) { constructor(channelControlHelper: ChannelControlHelper) {
this.childBalancer = new ChildLoadBalancerHandler(createChildChannelControlHelper(channelControlHelper, { this.childBalancer = new ChildLoadBalancerHandler(createChildChannelControlHelper(channelControlHelper, {
@ -373,7 +378,7 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
}, },
updateState: (connectivityState: ConnectivityState, picker: Picker) => { updateState: (connectivityState: ConnectivityState, picker: Picker) => {
if (connectivityState === ConnectivityState.READY) { if (connectivityState === ConnectivityState.READY) {
channelControlHelper.updateState(connectivityState, new OutlierDetectionPicker(picker)); channelControlHelper.updateState(connectivityState, new OutlierDetectionPicker(picker, this.isCountingEnabled()));
} else { } else {
channelControlHelper.updateState(connectivityState, picker); channelControlHelper.updateState(connectivityState, picker);
} }
@ -383,6 +388,12 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
clearInterval(this.ejectionTimer); clearInterval(this.ejectionTimer);
} }
private isCountingEnabled(): boolean {
return this.latestConfig !== null &&
(this.latestConfig.getSuccessRateEjectionConfig() !== null ||
this.latestConfig.getFailurePercentageEjectionConfig() !== null);
}
private getCurrentEjectionPercent() { private getCurrentEjectionPercent() {
let ejectionCount = 0; let ejectionCount = 0;
for (const mapEntry of this.addressMap.values()) { for (const mapEntry of this.addressMap.values()) {
@ -501,16 +512,26 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
} }
} }
private runChecks() { private switchAllBuckets() {
const ejectionTimestamp = new Date();
for (const mapEntry of this.addressMap.values()) { for (const mapEntry of this.addressMap.values()) {
mapEntry.counter.switchBuckets(); mapEntry.counter.switchBuckets();
} }
}
private startTimer(delayMs: number) {
this.ejectionTimer = setTimeout(() => this.runChecks(), delayMs);
}
private runChecks() {
const ejectionTimestamp = new Date();
this.switchAllBuckets();
if (!this.latestConfig) { if (!this.latestConfig) {
return; return;
} }
this.timerStartTime = ejectionTimestamp;
this.startTimer(this.latestConfig.getIntervalMs());
this.runSuccessRateCheck(ejectionTimestamp); this.runSuccessRateCheck(ejectionTimestamp);
this.runFailurePercentageCheck(ejectionTimestamp); this.runFailurePercentageCheck(ejectionTimestamp);
@ -561,10 +582,21 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
); );
this.childBalancer.updateAddressList(addressList, childPolicy, attributes); this.childBalancer.updateAddressList(addressList, childPolicy, attributes);
if (this.latestConfig === null || this.latestConfig.getIntervalMs() !== lbConfig.getIntervalMs()) { if (lbConfig.getSuccessRateEjectionConfig() || lbConfig.getFailurePercentageEjectionConfig()) {
clearInterval(this.ejectionTimer); if (this.timerStartTime) {
this.ejectionTimer = setInterval(() => this.runChecks(), lbConfig.getIntervalMs()); clearTimeout(this.ejectionTimer);
const remainingDelay = lbConfig.getIntervalMs() - ((new Date()).getTime() - this.timerStartTime.getTime());
this.startTimer(remainingDelay);
} else {
this.timerStartTime = new Date();
this.startTimer(lbConfig.getIntervalMs());
this.switchAllBuckets();
} }
} else {
this.timerStartTime = null;
clearTimeout(this.ejectionTimer);
}
this.latestConfig = lbConfig; this.latestConfig = lbConfig;
} }
exitIdle(): void { exitIdle(): void {
@ -574,6 +606,7 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
this.childBalancer.resetBackoff(); this.childBalancer.resetBackoff();
} }
destroy(): void { destroy(): void {
clearTimeout(this.ejectionTimer);
this.childBalancer.destroy(); this.childBalancer.destroy();
} }
getTypeName(): string { getTypeName(): string {