UNPKG

4.87 kBPlain TextView Raw
1/*
2 * Copyright 2020 gRPC authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18import {
19 LoadBalancer,
20 ChannelControlHelper,
21 LoadBalancingConfig,
22 createLoadBalancer,
23} from './load-balancer';
24import { SubchannelAddress } from './subchannel-address';
25import { ChannelOptions } from './channel-options';
26import { ConnectivityState } from './connectivity-state';
27import { Picker } from './picker';
28import { ChannelRef, SubchannelRef } from './channelz';
29import { SubchannelInterface } from './subchannel-interface';
30
31const TYPE_NAME = 'child_load_balancer_helper';
32
33export class ChildLoadBalancerHandler implements LoadBalancer {
34 private currentChild: LoadBalancer | null = null;
35 private pendingChild: LoadBalancer | null = null;
36
37 private ChildPolicyHelper = class {
38 private child: LoadBalancer | null = null;
39 constructor(private parent: ChildLoadBalancerHandler) {}
40 createSubchannel(
41 subchannelAddress: SubchannelAddress,
42 subchannelArgs: ChannelOptions
43 ): SubchannelInterface {
44 return this.parent.channelControlHelper.createSubchannel(
45 subchannelAddress,
46 subchannelArgs
47 );
48 }
49 updateState(connectivityState: ConnectivityState, picker: Picker): void {
50 if (this.calledByPendingChild()) {
51 if (connectivityState === ConnectivityState.CONNECTING) {
52 return;
53 }
54 this.parent.currentChild?.destroy();
55 this.parent.currentChild = this.parent.pendingChild;
56 this.parent.pendingChild = null;
57 } else if (!this.calledByCurrentChild()) {
58 return;
59 }
60 this.parent.channelControlHelper.updateState(connectivityState, picker);
61 }
62 requestReresolution(): void {
63 const latestChild = this.parent.pendingChild ?? this.parent.currentChild;
64 if (this.child === latestChild) {
65 this.parent.channelControlHelper.requestReresolution();
66 }
67 }
68 setChild(newChild: LoadBalancer) {
69 this.child = newChild;
70 }
71 addChannelzChild(child: ChannelRef | SubchannelRef) {
72 this.parent.channelControlHelper.addChannelzChild(child);
73 }
74 removeChannelzChild(child: ChannelRef | SubchannelRef) {
75 this.parent.channelControlHelper.removeChannelzChild(child);
76 }
77
78 private calledByPendingChild(): boolean {
79 return this.child === this.parent.pendingChild;
80 }
81 private calledByCurrentChild(): boolean {
82 return this.child === this.parent.currentChild;
83 }
84 };
85
86 constructor(private readonly channelControlHelper: ChannelControlHelper) {}
87
88 /**
89 * Prerequisites: lbConfig !== null and lbConfig.name is registered
90 * @param addressList
91 * @param lbConfig
92 * @param attributes
93 */
94 updateAddressList(
95 addressList: SubchannelAddress[],
96 lbConfig: LoadBalancingConfig,
97 attributes: { [key: string]: unknown }
98 ): void {
99 let childToUpdate: LoadBalancer;
100 if (
101 this.currentChild === null ||
102 this.currentChild.getTypeName() !== lbConfig.getLoadBalancerName()
103 ) {
104 const newHelper = new this.ChildPolicyHelper(this);
105 const newChild = createLoadBalancer(lbConfig, newHelper)!;
106 newHelper.setChild(newChild);
107 if (this.currentChild === null) {
108 this.currentChild = newChild;
109 childToUpdate = this.currentChild;
110 } else {
111 if (this.pendingChild) {
112 this.pendingChild.destroy();
113 }
114 this.pendingChild = newChild;
115 childToUpdate = this.pendingChild;
116 }
117 } else {
118 if (this.pendingChild === null) {
119 childToUpdate = this.currentChild;
120 } else {
121 childToUpdate = this.pendingChild;
122 }
123 }
124 childToUpdate.updateAddressList(addressList, lbConfig, attributes);
125 }
126 exitIdle(): void {
127 if (this.currentChild) {
128 this.currentChild.exitIdle();
129 if (this.pendingChild) {
130 this.pendingChild.exitIdle();
131 }
132 }
133 }
134 resetBackoff(): void {
135 if (this.currentChild) {
136 this.currentChild.resetBackoff();
137 if (this.pendingChild) {
138 this.pendingChild.resetBackoff();
139 }
140 }
141 }
142 destroy(): void {
143 if (this.currentChild) {
144 this.currentChild.destroy();
145 this.currentChild = null;
146 }
147 if (this.pendingChild) {
148 this.pendingChild.destroy();
149 this.pendingChild = null;
150 }
151 }
152 getTypeName(): string {
153 return TYPE_NAME;
154 }
155}