Skip to content

Commit 6c55cd0

Browse files
temawiejona86
authored andcommitted
util: Remove shutdown subchannels from OD tracking (#10683)
An OutlierDetectionLoadBalancer child load balancer might decided to shut down any subchannel it is tracking. We need to make sure that those subchannels are removed from the outlier detection tracker map to avoid a memory leak.
1 parent 43e98d0 commit 6c55cd0

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

util/src/main/java/io/grpc/util/OutlierDetectionLoadBalancer.java

+8
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,14 @@ public void start(SubchannelStateListener listener) {
257257
super.start(new OutlierDetectionSubchannelStateListener(listener));
258258
}
259259

260+
@Override
261+
public void shutdown() {
262+
if (addressTracker != null) {
263+
addressTracker.removeSubchannel(this);
264+
}
265+
super.shutdown();
266+
}
267+
260268
@Override
261269
public Attributes getAttributes() {
262270
if (addressTracker != null) {

util/src/test/java/io/grpc/util/OutlierDetectionLoadBalancerTest.java

+45-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public class OutlierDetectionLoadBalancerTest {
112112
@Captor
113113
private ArgumentCaptor<ConnectivityState> stateCaptor;
114114

115+
private FakeLoadBalancer fakeChildLb;
116+
115117
private final LoadBalancerProvider mockChildLbProvider = new StandardLoadBalancerProvider(
116118
"foo_policy") {
117119
@Override
@@ -123,7 +125,10 @@ public LoadBalancer newLoadBalancer(Helper helper) {
123125
"fake_policy") {
124126
@Override
125127
public LoadBalancer newLoadBalancer(Helper helper) {
126-
return new FakeLoadBalancer(helper);
128+
if (fakeChildLb == null) {
129+
fakeChildLb = new FakeLoadBalancer(helper);
130+
}
131+
return fakeChildLb;
127132
}
128133
};
129134
private final LoadBalancerProvider roundRobinLbProvider = new StandardLoadBalancerProvider(
@@ -266,6 +271,29 @@ public void acceptResolvedAddresses() {
266271
assertThat(task.getDelay(TimeUnit.NANOSECONDS)).isEqualTo(config.intervalNanos);
267272
}
268273

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+
269297
/**
270298
* Outlier detection first enabled, then removed.
271299
*/
@@ -1227,6 +1255,22 @@ public void handleNameResolutionError(Status error) {
12271255
public void shutdown() {
12281256
}
12291257

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+
12301274
void deliverSubchannelState(ConnectivityState state) {
12311275
SubchannelPicker picker = new SubchannelPicker() {
12321276
@Override

0 commit comments

Comments
 (0)