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 |
|
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 |
|
90 |
|
91 |
|
92 |
|
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 | }
|