xds: Copy data in least request to avoid picker data race · grpc/grpc-java@f4cc166
@@ -32,6 +32,7 @@
3232import io.grpc.ClientStreamTracer;
3333import io.grpc.ClientStreamTracer.StreamInfo;
3434import io.grpc.ConnectivityState;
35+import io.grpc.EquivalentAddressGroup;
3536import io.grpc.LoadBalancer;
3637import io.grpc.LoadBalancerProvider;
3738import io.grpc.Metadata;
@@ -153,28 +154,37 @@ private static AtomicInteger getInFlights(ChildLbState childLbState) {
153154154155@VisibleForTesting
155156static final class ReadyPicker extends SubchannelPicker {
156-private final List<ChildLbState> childLbStates; // non-empty
157+private final List<SubchannelPicker> childPickers; // non-empty
158+private final List<AtomicInteger> childInFlights; // 1:1 with childPickers
159+private final List<EquivalentAddressGroup> childEags; // 1:1 with childPickers
157160private final int choiceCount;
158161private final ThreadSafeRandom random;
159162private final int hashCode;
160163161164ReadyPicker(List<ChildLbState> childLbStates, int choiceCount, ThreadSafeRandom random) {
162165checkArgument(!childLbStates.isEmpty(), "empty list");
163-this.childLbStates = childLbStates;
166+this.childPickers = new ArrayList<>(childLbStates.size());
167+this.childInFlights = new ArrayList<>(childLbStates.size());
168+this.childEags = new ArrayList<>(childLbStates.size());
169+for (ChildLbState state : childLbStates) {
170+childPickers.add(state.getCurrentPicker());
171+childInFlights.add(getInFlights(state));
172+childEags.add(state.getEag());
173+ }
164174this.choiceCount = choiceCount;
165175this.random = checkNotNull(random, "random");
166176167177int sum = 0;
168-for (ChildLbState child : childLbStates) {
178+for (SubchannelPicker child : childPickers) {
169179sum += child.hashCode();
170180 }
171181this.hashCode = sum ^ choiceCount;
172182 }
173183174184@Override
175185public PickResult pickSubchannel(PickSubchannelArgs args) {
176-final ChildLbState childLbState = nextChildToUse();
177-PickResult childResult = childLbState.getCurrentPicker().pickSubchannel(args);
186+int child = nextChildToUse();
187+PickResult childResult = childPickers.get(child).pickSubchannel(args);
178188179189if (!childResult.getStatus().isOk() || childResult.getSubchannel() == null) {
180190return childResult;
@@ -186,33 +196,38 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
186196 } else {
187197// Wrap the subchannel
188198OutstandingRequestsTracingFactory factory =
189-new OutstandingRequestsTracingFactory(getInFlights(childLbState));
199+new OutstandingRequestsTracingFactory(childInFlights.get(child));
190200return PickResult.withSubchannel(childResult.getSubchannel(), factory);
191201 }
192202 }
193203194204@Override
195205public String toString() {
196206return MoreObjects.toStringHelper(ReadyPicker.class)
197- .add("list", childLbStates)
207+ .add("list", childPickers)
198208 .add("choiceCount", choiceCount)
199209 .toString();
200210 }
201211202-private ChildLbState nextChildToUse() {
203-ChildLbState candidate = childLbStates.get(random.nextInt(childLbStates.size()));
212+private int nextChildToUse() {
213+int candidate = random.nextInt(childPickers.size());
204214for (int i = 0; i < choiceCount - 1; ++i) {
205-ChildLbState sampled = childLbStates.get(random.nextInt(childLbStates.size()));
206-if (getInFlights(sampled).get() < getInFlights(candidate).get()) {
215+int sampled = random.nextInt(childPickers.size());
216+if (childInFlights.get(sampled).get() < childInFlights.get(candidate).get()) {
207217candidate = sampled;
208218 }
209219 }
210220return candidate;
211221 }
212222213223@VisibleForTesting
214-List<ChildLbState> getChildLbStates() {
215-return childLbStates;
224+List<SubchannelPicker> getChildPickers() {
225+return childPickers;
226+ }
227+228+@VisibleForTesting
229+List<EquivalentAddressGroup> getChildEags() {
230+return childEags;
216231 }
217232218233@Override
@@ -232,8 +247,8 @@ public boolean equals(Object o) {
232247// the lists cannot contain duplicate children
233248return hashCode == other.hashCode
234249&& choiceCount == other.choiceCount
235-&& childLbStates.size() == other.childLbStates.size()
236-&& new HashSet<>(childLbStates).containsAll(other.childLbStates);
250+&& childPickers.size() == other.childPickers.size()
251+&& new HashSet<>(childPickers).containsAll(other.childPickers);
237252 }
238253 }
239254