| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { |
| LoadBalancer, |
| ChannelControlHelper, |
| TypedLoadBalancingConfig, |
| createLoadBalancer, |
| } from './load-balancer'; |
| import { Endpoint, SubchannelAddress } from './subchannel-address'; |
| import { ChannelOptions } from './channel-options'; |
| import { ConnectivityState } from './connectivity-state'; |
| import { Picker } from './picker'; |
| import type { ChannelRef, SubchannelRef } from './channelz'; |
| import { SubchannelInterface } from './subchannel-interface'; |
| import { ChannelCredentials } from './channel-credentials'; |
|
|
| const TYPE_NAME = 'child_load_balancer_helper'; |
|
|
| export class ChildLoadBalancerHandler implements LoadBalancer { |
| private currentChild: LoadBalancer | null = null; |
| private pendingChild: LoadBalancer | null = null; |
| private latestConfig: TypedLoadBalancingConfig | null = null; |
|
|
| private ChildPolicyHelper = class { |
| private child: LoadBalancer | null = null; |
| constructor(private parent: ChildLoadBalancerHandler) {} |
| createSubchannel( |
| subchannelAddress: SubchannelAddress, |
| subchannelArgs: ChannelOptions, |
| credentialsOverride: ChannelCredentials | null |
| ): SubchannelInterface { |
| return this.parent.channelControlHelper.createSubchannel( |
| subchannelAddress, |
| subchannelArgs, |
| credentialsOverride |
| ); |
| } |
| updateState(connectivityState: ConnectivityState, picker: Picker): void { |
| if (this.calledByPendingChild()) { |
| if (connectivityState === ConnectivityState.CONNECTING) { |
| return; |
| } |
| this.parent.currentChild?.destroy(); |
| this.parent.currentChild = this.parent.pendingChild; |
| this.parent.pendingChild = null; |
| } else if (!this.calledByCurrentChild()) { |
| return; |
| } |
| this.parent.channelControlHelper.updateState(connectivityState, picker); |
| } |
| requestReresolution(): void { |
| const latestChild = this.parent.pendingChild ?? this.parent.currentChild; |
| if (this.child === latestChild) { |
| this.parent.channelControlHelper.requestReresolution(); |
| } |
| } |
| setChild(newChild: LoadBalancer) { |
| this.child = newChild; |
| } |
| addChannelzChild(child: ChannelRef | SubchannelRef) { |
| this.parent.channelControlHelper.addChannelzChild(child); |
| } |
| removeChannelzChild(child: ChannelRef | SubchannelRef) { |
| this.parent.channelControlHelper.removeChannelzChild(child); |
| } |
|
|
| private calledByPendingChild(): boolean { |
| return this.child === this.parent.pendingChild; |
| } |
| private calledByCurrentChild(): boolean { |
| return this.child === this.parent.currentChild; |
| } |
| }; |
|
|
| constructor( |
| private readonly channelControlHelper: ChannelControlHelper, |
| private readonly credentials: ChannelCredentials, |
| private readonly options: ChannelOptions |
| ) {} |
|
|
| protected configUpdateRequiresNewPolicyInstance( |
| oldConfig: TypedLoadBalancingConfig, |
| newConfig: TypedLoadBalancingConfig |
| ): boolean { |
| return oldConfig.getLoadBalancerName() !== newConfig.getLoadBalancerName(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| updateAddressList( |
| endpointList: Endpoint[], |
| lbConfig: TypedLoadBalancingConfig, |
| attributes: { [key: string]: unknown } |
| ): void { |
| let childToUpdate: LoadBalancer; |
| if ( |
| this.currentChild === null || |
| this.latestConfig === null || |
| this.configUpdateRequiresNewPolicyInstance(this.latestConfig, lbConfig) |
| ) { |
| const newHelper = new this.ChildPolicyHelper(this); |
| const newChild = createLoadBalancer(lbConfig, newHelper, this.credentials, this.options)!; |
| newHelper.setChild(newChild); |
| if (this.currentChild === null) { |
| this.currentChild = newChild; |
| childToUpdate = this.currentChild; |
| } else { |
| if (this.pendingChild) { |
| this.pendingChild.destroy(); |
| } |
| this.pendingChild = newChild; |
| childToUpdate = this.pendingChild; |
| } |
| } else { |
| if (this.pendingChild === null) { |
| childToUpdate = this.currentChild; |
| } else { |
| childToUpdate = this.pendingChild; |
| } |
| } |
| this.latestConfig = lbConfig; |
| childToUpdate.updateAddressList(endpointList, lbConfig, attributes); |
| } |
| exitIdle(): void { |
| if (this.currentChild) { |
| this.currentChild.exitIdle(); |
| if (this.pendingChild) { |
| this.pendingChild.exitIdle(); |
| } |
| } |
| } |
| resetBackoff(): void { |
| if (this.currentChild) { |
| this.currentChild.resetBackoff(); |
| if (this.pendingChild) { |
| this.pendingChild.resetBackoff(); |
| } |
| } |
| } |
| destroy(): void { |
| |
| |
| |
| |
| if (this.currentChild) { |
| this.currentChild.destroy(); |
| this.currentChild = null; |
| } |
| if (this.pendingChild) { |
| this.pendingChild.destroy(); |
| this.pendingChild = null; |
| } |
| } |
| getTypeName(): string { |
| return TYPE_NAME; |
| } |
| } |
|
|