Skip to content

Commit cc0d1a8

Browse files
committed
xds: Reduce per-endpoint memory from CDS
The locality name is long, so we don't want a different copy for each endpoint. The PathChains can use a similar amount of memory, so we also want to share them. The code no longer re-creates the EAG and Attributes, reducing garbage; that isn't as important as we're more concerned about retained memory in steady-state. But there was little reason for the waste, thus the cleanup. This fixes a bug for PathChains longer than 2 entries, which was never triggered as our PathChains are always 2 entries long. Previously the code would discard all but the first and last entry, with the same pathChain.next being repeatedly overwritten. Noticed the memory use when investigating b/507652503.
1 parent 17be0d3 commit cc0d1a8

3 files changed

Lines changed: 52 additions & 16 deletions

File tree

xds/src/main/java/io/grpc/xds/AddressFilter.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
import java.util.ArrayList;
2525
import java.util.Collections;
2626
import java.util.List;
27+
import java.util.ListIterator;
2728
import javax.annotation.Nullable;
2829

2930
final class AddressFilter {
3031
@ResolutionResultAttr
31-
private static final Attributes.Key<PathChain> PATH_CHAIN_KEY =
32+
static final Attributes.Key<PathChain> PATH_CHAIN_KEY =
3233
Attributes.Key.create("io.grpc.xds.AddressFilter.PATH_CHAIN_KEY");
3334

3435
// Prevent instantiation.
@@ -41,19 +42,25 @@ private AddressFilter() {}
4142
static EquivalentAddressGroup setPathFilter(EquivalentAddressGroup address, List<String> names) {
4243
checkNotNull(address, "address");
4344
checkNotNull(names, "names");
44-
Attributes.Builder attrBuilder = address.getAttributes().toBuilder().discard(PATH_CHAIN_KEY);
45-
PathChain pathChain = null;
46-
for (String name : names) {
47-
if (pathChain == null) {
48-
pathChain = new PathChain(name);
49-
attrBuilder.set(PATH_CHAIN_KEY, pathChain);
50-
} else {
51-
pathChain.next = new PathChain(name);
52-
}
53-
}
45+
Attributes.Builder attrBuilder = address.getAttributes().toBuilder()
46+
.set(PATH_CHAIN_KEY, createPathChain(names));
5447
return new EquivalentAddressGroup(address.getAddresses(), attrBuilder.build());
5548
}
5649

50+
/**
51+
* Creates a PathChain that can be set in an EquivalentAddressGroup's Attributes as a value of
52+
* PATH_CHAIN_KEY.
53+
*/
54+
@Nullable static PathChain createPathChain(List<String> names) {
55+
checkNotNull(names, "names");
56+
PathChain current = null;
57+
ListIterator<String> iter = names.listIterator(names.size());
58+
while (iter.hasPrevious()) {
59+
current = new PathChain(iter.previous(), current);
60+
}
61+
return current;
62+
}
63+
5764
/**
5865
* Returns the next level hierarchical addresses derived from the given hierarchical addresses
5966
* with the given filter name (any non-hierarchical addresses in the input will be ignored).
@@ -75,12 +82,13 @@ static List<EquivalentAddressGroup> filter(List<EquivalentAddressGroup> addresse
7582
return Collections.unmodifiableList(filteredAddresses);
7683
}
7784

78-
private static final class PathChain {
85+
static final class PathChain {
7986
final String name;
80-
@Nullable PathChain next;
87+
@Nullable final PathChain next;
8188

82-
PathChain(String name) {
89+
PathChain(String name, @Nullable PathChain next) {
8390
this.name = checkNotNull(name, "name");
91+
this.next = next;
8492
}
8593

8694
@Override

xds/src/main/java/io/grpc/xds/CdsLoadBalancer2.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,10 @@ StatusOr<ClusterResolutionResult> edsUpdateToResult(
332332
for (Locality locality : localityLbEndpoints.keySet()) {
333333
LocalityLbEndpoints localityLbInfo = localityLbEndpoints.get(locality);
334334
String priorityName = localityPriorityNames.get(locality);
335+
String localityName = localityName(locality);
336+
AddressFilter.PathChain pathChain =
337+
AddressFilter.createPathChain(Arrays.asList(priorityName, localityName));
338+
335339
boolean discard = true;
336340
// These sums _should_ fit in uint32, but XdsEndpointResource isn't actually verifying that
337341
// is true today. Since we are using long to avoid signedness trouble, the math happens to
@@ -367,7 +371,6 @@ StatusOr<ClusterResolutionResult> edsUpdateToResult(
367371
}
368372
}
369373

370-
String localityName = localityName(locality);
371374
Attributes attr =
372375
endpoint.eag().getAttributes().toBuilder()
373376
.set(InternalEquivalentAddressGroup.ATTR_BACKEND_SERVICE, clusterName)
@@ -377,6 +380,7 @@ StatusOr<ClusterResolutionResult> edsUpdateToResult(
377380
localityLbInfo.localityWeight())
378381
.set(io.grpc.xds.XdsAttributes.ATTR_SERVER_WEIGHT, weight)
379382
.set(XdsInternalAttributes.ATTR_ADDRESS_NAME, endpoint.hostname())
383+
.set(AddressFilter.PATH_CHAIN_KEY, pathChain)
380384
.build();
381385
EquivalentAddressGroup eag;
382386
if (discovery.isHttp11ProxyAvailable()) {
@@ -389,7 +393,6 @@ StatusOr<ClusterResolutionResult> edsUpdateToResult(
389393
} else {
390394
eag = new EquivalentAddressGroup(endpoint.eag().getAddresses(), attr);
391395
}
392-
eag = AddressFilter.setPathFilter(eag, Arrays.asList(priorityName, localityName));
393396
addresses.add(eag);
394397
}
395398
}

xds/src/test/java/io/grpc/xds/AddressFilterTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,29 @@ public void filterAddresses() {
5858
assertThat(filteredAddress0.getAttributes().get(key1)).isEqualTo("value1");
5959
assertThat(filteredAddress1.getAddresses()).containsExactlyElementsIn(eag3.getAddresses());
6060
}
61+
62+
@Test
63+
public void longerPathChain() {
64+
List<EquivalentAddressGroup> addresses = Arrays.asList(
65+
newEag(new InetSocketAddress(8000), Arrays.asList("A", "B", "C")),
66+
newEag(new InetSocketAddress(8001), Arrays.asList("Z", "B", "C")),
67+
newEag(new InetSocketAddress(8002), Arrays.asList("A", "Z", "C")),
68+
newEag(new InetSocketAddress(8003), Arrays.asList("A", "B", "Z")));
69+
addresses = AddressFilter.filter(addresses, "A");
70+
assertThat(addresses).hasSize(3);
71+
72+
addresses = AddressFilter.filter(addresses, "B");
73+
assertThat(addresses).hasSize(2);
74+
75+
addresses = AddressFilter.filter(addresses, "C");
76+
assertThat(addresses).hasSize(1);
77+
assertThat(addresses.get(0).getAddresses()).containsExactly(new InetSocketAddress(8000));
78+
79+
addresses = AddressFilter.filter(addresses, "D");
80+
assertThat(addresses).hasSize(0);
81+
}
82+
83+
private static EquivalentAddressGroup newEag(InetSocketAddress address, List<String> names) {
84+
return AddressFilter.setPathFilter(new EquivalentAddressGroup(address), names);
85+
}
6186
}

0 commit comments

Comments
 (0)