1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | import {
|
19 | LoadBalancer,
|
20 | ChannelControlHelper,
|
21 | LoadBalancingConfig,
|
22 | createLoadBalancer,
|
23 | } from './load-balancer';
|
24 | import { SubchannelAddress } from './subchannel-address';
|
25 | import { ChannelOptions } from './channel-options';
|
26 | import { ConnectivityState } from './connectivity-state';
|
27 | import { Picker } from './picker';
|
28 | import { ChannelRef, SubchannelRef } from './channelz';
|
29 | import { SubchannelInterface } from './subchannel-interface';
|
30 |
|
31 | const TYPE_NAME = 'child_load_balancer_helper';
|
32 |
|
33 | export class ChildLoadBalancerHandler implements LoadBalancer {
|
34 | private currentChild: LoadBalancer | null = null;
|
35 | private pendingChild: LoadBalancer | null = null;
|
36 | private latestConfig: LoadBalancingConfig | null = null;
|
37 |
|
38 | private ChildPolicyHelper = class {
|
39 | private child: LoadBalancer | null = null;
|
40 | constructor(private parent: ChildLoadBalancerHandler) {}
|
41 | createSubchannel(
|
42 | subchannelAddress: SubchannelAddress,
|
43 | subchannelArgs: ChannelOptions
|
44 | ): SubchannelInterface {
|
45 | return this.parent.channelControlHelper.createSubchannel(
|
46 | subchannelAddress,
|
47 | subchannelArgs
|
48 | );
|
49 | }
|
50 | updateState(connectivityState: ConnectivityState, picker: Picker): void {
|
51 | if (this.calledByPendingChild()) {
|
52 | if (connectivityState === ConnectivityState.CONNECTING) {
|
53 | return;
|
54 | }
|
55 | this.parent.currentChild?.destroy();
|
56 | this.parent.currentChild = this.parent.pendingChild;
|
57 | this.parent.pendingChild = null;
|
58 | } else if (!this.calledByCurrentChild()) {
|
59 | return;
|
60 | }
|
61 | this.parent.channelControlHelper.updateState(connectivityState, picker);
|
62 | }
|
63 | requestReresolution(): void {
|
64 | const latestChild = this.parent.pendingChild ?? this.parent.currentChild;
|
65 | if (this.child === latestChild) {
|
66 | this.parent.channelControlHelper.requestReresolution();
|
67 | }
|
68 | }
|
69 | setChild(newChild: LoadBalancer) {
|
70 | this.child = newChild;
|
71 | }
|
72 | addChannelzChild(child: ChannelRef | SubchannelRef) {
|
73 | this.parent.channelControlHelper.addChannelzChild(child);
|
74 | }
|
75 | removeChannelzChild(child: ChannelRef | SubchannelRef) {
|
76 | this.parent.channelControlHelper.removeChannelzChild(child);
|
77 | }
|
78 |
|
79 | private calledByPendingChild(): boolean {
|
80 | return this.child === this.parent.pendingChild;
|
81 | }
|
82 | private calledByCurrentChild(): boolean {
|
83 | return this.child === this.parent.currentChild;
|
84 | }
|
85 | };
|
86 |
|
87 | constructor(private readonly channelControlHelper: ChannelControlHelper) {}
|
88 |
|
89 | protected configUpdateRequiresNewPolicyInstance(
|
90 | oldConfig: LoadBalancingConfig,
|
91 | newConfig: LoadBalancingConfig
|
92 | ): boolean {
|
93 | return oldConfig.getLoadBalancerName() !== newConfig.getLoadBalancerName();
|
94 | }
|
95 |
|
96 | |
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | updateAddressList(
|
103 | addressList: SubchannelAddress[],
|
104 | lbConfig: LoadBalancingConfig,
|
105 | attributes: { [key: string]: unknown }
|
106 | ): void {
|
107 | let childToUpdate: LoadBalancer;
|
108 | if (
|
109 | this.currentChild === null ||
|
110 | this.latestConfig === null ||
|
111 | this.configUpdateRequiresNewPolicyInstance(this.latestConfig, lbConfig)
|
112 | ) {
|
113 | const newHelper = new this.ChildPolicyHelper(this);
|
114 | const newChild = createLoadBalancer(lbConfig, newHelper)!;
|
115 | newHelper.setChild(newChild);
|
116 | if (this.currentChild === null) {
|
117 | this.currentChild = newChild;
|
118 | childToUpdate = this.currentChild;
|
119 | } else {
|
120 | if (this.pendingChild) {
|
121 | this.pendingChild.destroy();
|
122 | }
|
123 | this.pendingChild = newChild;
|
124 | childToUpdate = this.pendingChild;
|
125 | }
|
126 | } else {
|
127 | if (this.pendingChild === null) {
|
128 | childToUpdate = this.currentChild;
|
129 | } else {
|
130 | childToUpdate = this.pendingChild;
|
131 | }
|
132 | }
|
133 | this.latestConfig = lbConfig;
|
134 | childToUpdate.updateAddressList(addressList, lbConfig, attributes);
|
135 | }
|
136 | exitIdle(): void {
|
137 | if (this.currentChild) {
|
138 | this.currentChild.exitIdle();
|
139 | if (this.pendingChild) {
|
140 | this.pendingChild.exitIdle();
|
141 | }
|
142 | }
|
143 | }
|
144 | resetBackoff(): void {
|
145 | if (this.currentChild) {
|
146 | this.currentChild.resetBackoff();
|
147 | if (this.pendingChild) {
|
148 | this.pendingChild.resetBackoff();
|
149 | }
|
150 | }
|
151 | }
|
152 | destroy(): void {
|
153 | |
154 |
|
155 |
|
156 |
|
157 | if (this.currentChild) {
|
158 | this.currentChild.destroy();
|
159 | this.currentChild = null;
|
160 | }
|
161 | if (this.pendingChild) {
|
162 | this.pendingChild.destroy();
|
163 | this.pendingChild = null;
|
164 | }
|
165 | }
|
166 | getTypeName(): string {
|
167 | return TYPE_NAME;
|
168 | }
|
169 | }
|