Skip to content

Commit 063b5c9

Browse files
coeuvrecopybara-github
authored andcommitted
Remote: Limit max number of gRPC connections by --remote_max_connections.
`--remote_max_connections` is only applied to HTTP remote cache. This PR makes it apply to gRPC cache/executor as well. Note that `--remote_max_connections` limits the number of concurrent connections. For HTTP remote cache, one connection could handle one request at one time. For gRPC remote cache/executor, one connection could handle 100+ concurrent requests. So the default value `100` means we could make up to `100` concurrent requests for HTTP remote cache or `10000+` concurrent requests for gRPC remote cache/executor. Fixes: bazelbuild#14178. Closes bazelbuild#14202. PiperOrigin-RevId: 410249542
1 parent ed68933 commit 063b5c9

File tree

4 files changed

+52
-21
lines changed

4 files changed

+52
-21
lines changed

src/main/java/com/google/devtools/build/lib/remote/ReferenceCountedChannel.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,13 @@ public ReferenceCounted touch(Object o) {
6262
private final AtomicReference<String> authorityRef = new AtomicReference<>();
6363

6464
public ReferenceCountedChannel(ChannelConnectionFactory connectionFactory) {
65+
this(connectionFactory, /*maxConnections=*/ 0);
66+
}
67+
68+
public ReferenceCountedChannel(ChannelConnectionFactory connectionFactory, int maxConnections) {
6569
this.dynamicConnectionPool =
66-
new DynamicConnectionPool(connectionFactory, connectionFactory.maxConcurrency());
70+
new DynamicConnectionPool(
71+
connectionFactory, connectionFactory.maxConcurrency(), maxConnections);
6772
}
6873

6974
public boolean isShutdown() {
@@ -87,12 +92,12 @@ public void start(Listener<RespT> responseListener, Metadata headers) {
8792
responseListener) {
8893
@Override
8994
public void onClose(Status status, Metadata trailers) {
90-
super.onClose(status, trailers);
91-
9295
try {
9396
connection.close();
9497
} catch (IOException e) {
9598
throw new AssertionError(e.getMessage(), e);
99+
} finally {
100+
super.onClose(status, trailers);
96101
}
97102
}
98103
},

src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
352352
// based on the resolved IPs of that server. We assume servers normally have 2 IPs. So the
353353
// max concurrency per connection is 100.
354354
int maxConcurrencyPerConnection = 100;
355+
int maxConnections = 0;
356+
if (remoteOptions.remoteMaxConnections > 0) {
357+
maxConnections = remoteOptions.remoteMaxConnections;
358+
}
355359

356360
if (enableRemoteExecution) {
357361
ImmutableList.Builder<ClientInterceptor> interceptors = ImmutableList.builder();
@@ -367,7 +371,8 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
367371
remoteOptions.remoteProxy,
368372
authAndTlsOptions,
369373
interceptors.build(),
370-
maxConcurrencyPerConnection));
374+
maxConcurrencyPerConnection),
375+
maxConnections);
371376

372377
// Create a separate channel if --remote_executor and --remote_cache point to different
373378
// endpoints.
@@ -390,7 +395,8 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
390395
remoteOptions.remoteProxy,
391396
authAndTlsOptions,
392397
interceptors.build(),
393-
maxConcurrencyPerConnection));
398+
maxConcurrencyPerConnection),
399+
maxConnections);
394400
}
395401

396402
if (enableRemoteDownloader) {
@@ -411,7 +417,8 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
411417
remoteOptions.remoteProxy,
412418
authAndTlsOptions,
413419
interceptors.build(),
414-
maxConcurrencyPerConnection));
420+
maxConcurrencyPerConnection),
421+
maxConnections);
415422
}
416423
}
417424

src/main/java/com/google/devtools/build/lib/remote/grpc/DynamicConnectionPool.java

+27-12
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
public class DynamicConnectionPool implements ConnectionPool {
3131
private final ConnectionFactory connectionFactory;
3232
private final int maxConcurrencyPerConnection;
33+
private final int maxConnections;
3334
private final AtomicBoolean closed = new AtomicBoolean(false);
3435

3536
@GuardedBy("this")
@@ -40,8 +41,14 @@ public class DynamicConnectionPool implements ConnectionPool {
4041

4142
public DynamicConnectionPool(
4243
ConnectionFactory connectionFactory, int maxConcurrencyPerConnection) {
44+
this(connectionFactory, maxConcurrencyPerConnection, /*maxConnections=*/ 0);
45+
}
46+
47+
public DynamicConnectionPool(
48+
ConnectionFactory connectionFactory, int maxConcurrencyPerConnection, int maxConnections) {
4349
this.connectionFactory = connectionFactory;
4450
this.maxConcurrencyPerConnection = maxConcurrencyPerConnection;
51+
this.maxConnections = maxConnections;
4552
this.factories = new ArrayList<>();
4653
}
4754

@@ -61,12 +68,19 @@ public void close() throws IOException {
6168
}
6269
}
6370

71+
@GuardedBy("this")
72+
private SharedConnectionFactory nextFactory() {
73+
int index = Math.abs(indexTicker % factories.size());
74+
indexTicker += 1;
75+
return factories.get(index);
76+
}
77+
6478
/**
65-
* Performs a simple round robin on the list of {@link SharedConnectionFactory} and return one
66-
* having available connections at this moment.
79+
* Performs a simple round robin on the list of {@link SharedConnectionFactory}.
6780
*
68-
* <p>If no factory has available connections, it will create a new {@link
69-
* SharedConnectionFactory}.
81+
* <p>This will try to find a factory that has available connections at this moment. If no factory
82+
* has available connections, and the number of factories is less than {@link #maxConnections}, it
83+
* will create a new {@link SharedConnectionFactory}.
7084
*/
7185
private SharedConnectionFactory nextAvailableFactory() {
7286
if (closed.get()) {
@@ -75,19 +89,20 @@ private SharedConnectionFactory nextAvailableFactory() {
7589

7690
synchronized (this) {
7791
for (int times = 0; times < factories.size(); ++times) {
78-
int index = Math.abs(indexTicker % factories.size());
79-
indexTicker += 1;
80-
81-
SharedConnectionFactory factory = factories.get(index);
92+
SharedConnectionFactory factory = nextFactory();
8293
if (factory.numAvailableConnections() > 0) {
8394
return factory;
8495
}
8596
}
8697

87-
SharedConnectionFactory factory =
88-
new SharedConnectionFactory(connectionFactory, maxConcurrencyPerConnection);
89-
factories.add(factory);
90-
return factory;
98+
if (maxConnections <= 0 || factories.size() < maxConnections) {
99+
SharedConnectionFactory factory =
100+
new SharedConnectionFactory(connectionFactory, maxConcurrencyPerConnection);
101+
factories.add(factory);
102+
return factory;
103+
} else {
104+
return nextFactory();
105+
}
91106
}
92107
}
93108

src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,13 @@ public final class RemoteOptions extends OptionsBase {
6363
documentationCategory = OptionDocumentationCategory.REMOTE,
6464
effectTags = {OptionEffectTag.HOST_MACHINE_RESOURCE_OPTIMIZATIONS},
6565
help =
66-
"The max. number of concurrent network connections to the remote cache/executor. By "
67-
+ "default Bazel limits the number of TCP connections to 100. Setting this flag to "
68-
+ "0 will make Bazel choose the number of connections automatically.")
66+
"Limit the max number of concurrent connections to remote cache/executor. By default the"
67+
+ " value is 100. Setting this to 0 means no limitation.\n"
68+
+ "For HTTP remote cache, one TCP connection could handle one request at one time, so"
69+
+ " Bazel could make up to --remote_max_connections concurrent requests.\n"
70+
+ "For gRPC remote cache/executor, one gRPC channel could usually handle 100+"
71+
+ " concurrent requests, so Bazel could make around `--remote_max_connections * 100`"
72+
+ " concurrent requests.")
6973
public int remoteMaxConnections;
7074

7175
@Option(

0 commit comments

Comments
 (0)