@@ -112,6 +112,8 @@ public class OutlierDetectionLoadBalancerTest {
112
112
@ Captor
113
113
private ArgumentCaptor <ConnectivityState > stateCaptor ;
114
114
115
+ private FakeLoadBalancer fakeChildLb ;
116
+
115
117
private final LoadBalancerProvider mockChildLbProvider = new StandardLoadBalancerProvider (
116
118
"foo_policy" ) {
117
119
@ Override
@@ -123,7 +125,10 @@ public LoadBalancer newLoadBalancer(Helper helper) {
123
125
"fake_policy" ) {
124
126
@ Override
125
127
public LoadBalancer newLoadBalancer (Helper helper ) {
126
- return new FakeLoadBalancer (helper );
128
+ if (fakeChildLb == null ) {
129
+ fakeChildLb = new FakeLoadBalancer (helper );
130
+ }
131
+ return fakeChildLb ;
127
132
}
128
133
};
129
134
private final LoadBalancerProvider roundRobinLbProvider = new StandardLoadBalancerProvider (
@@ -266,6 +271,29 @@ public void acceptResolvedAddresses() {
266
271
assertThat (task .getDelay (TimeUnit .NANOSECONDS )).isEqualTo (config .intervalNanos );
267
272
}
268
273
274
+ /**
275
+ * The child LB might recreate subchannels leaving the ones we are tracking
276
+ * orphaned in the address tracker. Make sure subchannels that are shut down get
277
+ * removed from the tracker.
278
+ */
279
+ @ Test
280
+ public void childLbRecreatesSubchannels () {
281
+ OutlierDetectionLoadBalancerConfig config = new OutlierDetectionLoadBalancerConfig .Builder ()
282
+ .setSuccessRateEjection (new SuccessRateEjection .Builder ().build ())
283
+ .setChildPolicy (new PolicySelection (fakeLbProvider , null )).build ();
284
+
285
+ loadBalancer .acceptResolvedAddresses (buildResolvedAddress (config , servers .get (0 )));
286
+
287
+ assertThat (loadBalancer .trackerMap ).hasSize (1 );
288
+ AddressTracker addressTracker = (AddressTracker ) loadBalancer .trackerMap .values ().toArray ()[0 ];
289
+ assertThat (addressTracker ).isNotNull ();
290
+ OutlierDetectionSubchannel trackedSubchannel
291
+ = (OutlierDetectionSubchannel ) addressTracker .getSubchannels ().toArray ()[0 ];
292
+
293
+ fakeChildLb .recreateSubchannels ();
294
+ assertThat (addressTracker .getSubchannels ()).doesNotContain (trackedSubchannel );
295
+ }
296
+
269
297
/**
270
298
* Outlier detection first enabled, then removed.
271
299
*/
@@ -1227,6 +1255,22 @@ public void handleNameResolutionError(Status error) {
1227
1255
public void shutdown () {
1228
1256
}
1229
1257
1258
+ // Simulates a situation where a load balancer might recreate some of the subchannels it is
1259
+ // tracking even if acceptResolvedAddresses() has not been called.
1260
+ void recreateSubchannels () {
1261
+ List <Subchannel > newSubchannelList = new ArrayList <>(subchannelList .size ());
1262
+ for (Subchannel subchannel : subchannelList ) {
1263
+ Subchannel newSubchannel = helper
1264
+ .createSubchannel (
1265
+ CreateSubchannelArgs .newBuilder ().setAddresses (subchannel .getAddresses ()).build ());
1266
+ newSubchannel .start (mock (SubchannelStateListener .class ));
1267
+ subchannel .shutdown ();
1268
+ newSubchannelList .add (newSubchannel );
1269
+ }
1270
+ subchannelList = newSubchannelList ;
1271
+ deliverSubchannelState (READY );
1272
+ }
1273
+
1230
1274
void deliverSubchannelState (ConnectivityState state ) {
1231
1275
SubchannelPicker picker = new SubchannelPicker () {
1232
1276
@ Override
0 commit comments