xds: Copy data in least request to avoid picker data race · grpc/grpc-java@f4cc166

@@ -32,6 +32,7 @@

3232

import io.grpc.ClientStreamTracer;

3333

import io.grpc.ClientStreamTracer.StreamInfo;

3434

import io.grpc.ConnectivityState;

35+

import io.grpc.EquivalentAddressGroup;

3536

import io.grpc.LoadBalancer;

3637

import io.grpc.LoadBalancerProvider;

3738

import io.grpc.Metadata;

@@ -153,28 +154,37 @@ private static AtomicInteger getInFlights(ChildLbState childLbState) {

153154154155

@VisibleForTesting

155156

static 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

157160

private final int choiceCount;

158161

private final ThreadSafeRandom random;

159162

private final int hashCode;

160163161164

ReadyPicker(List<ChildLbState> childLbStates, int choiceCount, ThreadSafeRandom random) {

162165

checkArgument(!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+

}

164174

this.choiceCount = choiceCount;

165175

this.random = checkNotNull(random, "random");

166176167177

int sum = 0;

168-

for (ChildLbState child : childLbStates) {

178+

for (SubchannelPicker child : childPickers) {

169179

sum += child.hashCode();

170180

}

171181

this.hashCode = sum ^ choiceCount;

172182

}

173183174184

@Override

175185

public 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);

178188179189

if (!childResult.getStatus().isOk() || childResult.getSubchannel() == null) {

180190

return childResult;

@@ -186,33 +196,38 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {

186196

} else {

187197

// Wrap the subchannel

188198

OutstandingRequestsTracingFactory factory =

189-

new OutstandingRequestsTracingFactory(getInFlights(childLbState));

199+

new OutstandingRequestsTracingFactory(childInFlights.get(child));

190200

return PickResult.withSubchannel(childResult.getSubchannel(), factory);

191201

}

192202

}

193203194204

@Override

195205

public String toString() {

196206

return 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());

204214

for (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()) {

207217

candidate = sampled;

208218

}

209219

}

210220

return 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

233248

return 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