diff options
| author | altaf-creator <dev@altafcreator.com> | 2025-11-09 11:15:19 +0800 |
|---|---|---|
| committer | altaf-creator <dev@altafcreator.com> | 2025-11-09 11:15:19 +0800 |
| commit | 8eff962cab608341a6f2fedc640a0e32d96f26e2 (patch) | |
| tree | 05534d1a720ddc3691d346c69b4972555820a061 /frontend-old/node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts | |
pain
Diffstat (limited to 'frontend-old/node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts')
| -rw-r--r-- | frontend-old/node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/frontend-old/node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts b/frontend-old/node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts new file mode 100644 index 0000000..062aa9f --- /dev/null +++ b/frontend-old/node_modules/@grpc/grpc-js/src/load-balancer-round-robin.ts @@ -0,0 +1,249 @@ +/* + * Copyright 2019 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. + * + */ + +import { + LoadBalancer, + ChannelControlHelper, + LoadBalancingConfig, + registerLoadBalancerType, +} from './load-balancer'; +import { ConnectivityState } from './connectivity-state'; +import { + QueuePicker, + Picker, + PickArgs, + CompletePickResult, + PickResultType, + UnavailablePicker, +} from './picker'; +import { + SubchannelAddress, + subchannelAddressToString, +} from './subchannel-address'; +import * as logging from './logging'; +import { LogVerbosity } from './constants'; +import { + ConnectivityStateListener, + SubchannelInterface, +} from './subchannel-interface'; + +const TRACER_NAME = 'round_robin'; + +function trace(text: string): void { + logging.trace(LogVerbosity.DEBUG, TRACER_NAME, text); +} + +const TYPE_NAME = 'round_robin'; + +class RoundRobinLoadBalancingConfig implements LoadBalancingConfig { + getLoadBalancerName(): string { + return TYPE_NAME; + } + + constructor() {} + + toJsonObject(): object { + return { + [TYPE_NAME]: {}, + }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static createFromJson(obj: any) { + return new RoundRobinLoadBalancingConfig(); + } +} + +class RoundRobinPicker implements Picker { + constructor( + private readonly subchannelList: SubchannelInterface[], + private nextIndex = 0 + ) {} + + pick(pickArgs: PickArgs): CompletePickResult { + const pickedSubchannel = this.subchannelList[this.nextIndex]; + this.nextIndex = (this.nextIndex + 1) % this.subchannelList.length; + return { + pickResultType: PickResultType.COMPLETE, + subchannel: pickedSubchannel, + status: null, + onCallStarted: null, + onCallEnded: null, + }; + } + + /** + * Check what the next subchannel returned would be. Used by the load + * balancer implementation to preserve this part of the picker state if + * possible when a subchannel connects or disconnects. + */ + peekNextSubchannel(): SubchannelInterface { + return this.subchannelList[this.nextIndex]; + } +} + +export class RoundRobinLoadBalancer implements LoadBalancer { + private subchannels: SubchannelInterface[] = []; + + private currentState: ConnectivityState = ConnectivityState.IDLE; + + private subchannelStateListener: ConnectivityStateListener; + + private currentReadyPicker: RoundRobinPicker | null = null; + + private lastError: string | null = null; + + constructor(private readonly channelControlHelper: ChannelControlHelper) { + this.subchannelStateListener = ( + subchannel: SubchannelInterface, + previousState: ConnectivityState, + newState: ConnectivityState, + keepaliveTime: number, + errorMessage?: string + ) => { + this.calculateAndUpdateState(); + if ( + newState === ConnectivityState.TRANSIENT_FAILURE || + newState === ConnectivityState.IDLE + ) { + if (errorMessage) { + this.lastError = errorMessage; + } + this.channelControlHelper.requestReresolution(); + subchannel.startConnecting(); + } + }; + } + + private countSubchannelsWithState(state: ConnectivityState) { + return this.subchannels.filter( + subchannel => subchannel.getConnectivityState() === state + ).length; + } + + private calculateAndUpdateState() { + if (this.countSubchannelsWithState(ConnectivityState.READY) > 0) { + const readySubchannels = this.subchannels.filter( + subchannel => + subchannel.getConnectivityState() === ConnectivityState.READY + ); + let index = 0; + if (this.currentReadyPicker !== null) { + index = readySubchannels.indexOf( + this.currentReadyPicker.peekNextSubchannel() + ); + if (index < 0) { + index = 0; + } + } + this.updateState( + ConnectivityState.READY, + new RoundRobinPicker(readySubchannels, index) + ); + } else if ( + this.countSubchannelsWithState(ConnectivityState.CONNECTING) > 0 + ) { + this.updateState(ConnectivityState.CONNECTING, new QueuePicker(this)); + } else if ( + this.countSubchannelsWithState(ConnectivityState.TRANSIENT_FAILURE) > 0 + ) { + this.updateState( + ConnectivityState.TRANSIENT_FAILURE, + new UnavailablePicker({details: `No connection established. Last error: ${this.lastError}`}) + ); + } else { + this.updateState(ConnectivityState.IDLE, new QueuePicker(this)); + } + } + + private updateState(newState: ConnectivityState, picker: Picker) { + trace( + ConnectivityState[this.currentState] + + ' -> ' + + ConnectivityState[newState] + ); + if (newState === ConnectivityState.READY) { + this.currentReadyPicker = picker as RoundRobinPicker; + } else { + this.currentReadyPicker = null; + } + this.currentState = newState; + this.channelControlHelper.updateState(newState, picker); + } + + private resetSubchannelList() { + for (const subchannel of this.subchannels) { + subchannel.removeConnectivityStateListener(this.subchannelStateListener); + subchannel.unref(); + this.channelControlHelper.removeChannelzChild( + subchannel.getChannelzRef() + ); + } + this.subchannels = []; + } + + updateAddressList( + addressList: SubchannelAddress[], + lbConfig: LoadBalancingConfig + ): void { + this.resetSubchannelList(); + trace( + 'Connect to address list ' + + addressList.map(address => subchannelAddressToString(address)) + ); + this.subchannels = addressList.map(address => + this.channelControlHelper.createSubchannel(address, {}) + ); + for (const subchannel of this.subchannels) { + subchannel.ref(); + subchannel.addConnectivityStateListener(this.subchannelStateListener); + this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef()); + const subchannelState = subchannel.getConnectivityState(); + if ( + subchannelState === ConnectivityState.IDLE || + subchannelState === ConnectivityState.TRANSIENT_FAILURE + ) { + subchannel.startConnecting(); + } + } + this.calculateAndUpdateState(); + } + + exitIdle(): void { + for (const subchannel of this.subchannels) { + subchannel.startConnecting(); + } + } + resetBackoff(): void { + /* The pick first load balancer does not have a connection backoff, so this + * does nothing */ + } + destroy(): void { + this.resetSubchannelList(); + } + getTypeName(): string { + return TYPE_NAME; + } +} + +export function setup() { + registerLoadBalancerType( + TYPE_NAME, + RoundRobinLoadBalancer, + RoundRobinLoadBalancingConfig + ); +} |
