xds: Fix WeakReference bug in SharedCallCounterMap (#8466)

Fixes #8397.
#8397 is caused by mistakenly clearing up a map entry right after the entry is recreated after gc. Reproduced in regression test.
This commit is contained in:
ZHANG Dapeng 2021-09-02 10:25:15 -07:00 committed by GitHub
parent 2faa748797
commit 07747c59a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 2 deletions

View File

@ -58,8 +58,14 @@ final class SharedCallCounterMap implements CallCounterProvider {
counters.put(cluster, clusterCounters);
}
CounterReference ref = clusterCounters.get(edsServiceName);
AtomicLong counter;
if (ref == null || (counter = ref.get()) == null) {
AtomicLong counter = null;
if (ref != null) {
counter = ref.get();
if (counter == null) {
ref.enqueue();
}
}
if (counter == null) {
counter = new AtomicLong();
ref = new CounterReference(counter, refQueue, cluster, edsServiceName);
clusterCounters.put(edsServiceName, ref);
@ -73,6 +79,9 @@ final class SharedCallCounterMap implements CallCounterProvider {
CounterReference ref;
while ((ref = (CounterReference) refQueue.poll()) != null) {
Map<String, CounterReference> clusterCounter = counters.get(ref.cluster);
if (clusterCounter.get(ref.edsServiceName) != ref) {
continue;
}
clusterCounter.remove(ref.edsServiceName);
if (clusterCounter.isEmpty()) {
counters.remove(ref.cluster);

View File

@ -62,4 +62,22 @@ public class SharedCallCounterMapTest {
map.cleanQueue();
assertThat(counters).isEmpty();
}
@Test
public void gcAndRecreate() {
@SuppressWarnings("UnusedVariable") // assign to null for GC only
AtomicLong counter = map.getOrCreate(CLUSTER, EDS_SERVICE_NAME);
final CounterReference ref = counters.get(CLUSTER).get(EDS_SERVICE_NAME);
assertThat(counter.get()).isEqualTo(0);
counter = null;
GcFinalization.awaitDone(new FinalizationPredicate() {
@Override
public boolean isDone() {
return ref.isEnqueued();
}
});
map.getOrCreate(CLUSTER, EDS_SERVICE_NAME);
assertThat(counters.get(CLUSTER)).isNotNull();
assertThat(counters.get(CLUSTER).get(EDS_SERVICE_NAME)).isNotNull();
}
}