Rate limit the number of gsutil.py subprocesses tools/approve_results.dart spawns
Without any rate limiting, the `tools/approve_results.dart` tool can
render a machine unusable (e.g. when running with
`tools/approve_results.dart -b '*'`).
Unfortunately each gsutil.py subprocess might launch many more
subprocesses on it's own due to us passing the '-m' flag.
Change-Id: I06f4f54fb87a107d83d34205dafe03a8c73d747d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106902
Reviewed-by: Jonas Termansen <sortie@google.com>
Auto-Submit: Martin Kustermann <kustermann@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/tools/bots/results.dart b/tools/bots/results.dart
index 65ae36f..1848db5 100644
--- a/tools/bots/results.dart
+++ b/tools/bots/results.dart
@@ -7,6 +7,9 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
+import 'dart:math';
+
+import 'package:pool/pool.dart';
/// The path to the gsutil script.
String gsutilPy;
@@ -18,30 +21,36 @@
const approvedResultsStoragePath =
"gs://dart-test-results-approved-results/builders";
+/// Limit the number of concurrent subprocesses by half the number of cores.
+final gsutilPool = new Pool(max(1, Platform.numberOfProcessors ~/ 2));
+
/// Runs gsutil with the provided [arguments] and returns the standard output.
/// Returns null if the requested URL didn't exist.
Future<String> runGsutil(List<String> arguments) async {
- final processResult = await Process.run(
- "python", [gsutilPy]..addAll(arguments),
- runInShell: Platform.isWindows);
- if (processResult.exitCode != 0) {
- if (processResult.exitCode == 1 &&
- processResult.stderr.contains("No URLs matched") ||
- processResult.stderr.contains("One or more URLs matched no objects")) {
- return null;
+ return gsutilPool.withResource(() async {
+ final processResult = await Process.run(
+ "python", [gsutilPy]..addAll(arguments),
+ runInShell: Platform.isWindows);
+ if (processResult.exitCode != 0) {
+ if (processResult.exitCode == 1 &&
+ processResult.stderr.contains("No URLs matched") ||
+ processResult.stderr
+ .contains("One or more URLs matched no objects")) {
+ return null;
+ }
+ String error = "Failed to run: python $gsutilPy $arguments\n"
+ "exitCode: ${processResult.exitCode}\n"
+ "stdout:\n${processResult.stdout}\n"
+ "stderr:\n${processResult.stderr}";
+ if (processResult.exitCode == 1 &&
+ processResult.stderr.contains("401 Anonymous caller")) {
+ error =
+ "\n\nYou need to authenticate by running:\npython $gsutilPy config\n";
+ }
+ throw new Exception(error);
}
- String error = "Failed to run: python $gsutilPy $arguments\n"
- "exitCode: ${processResult.exitCode}\n"
- "stdout:\n${processResult.stdout}\n"
- "stderr:\n${processResult.stderr}";
- if (processResult.exitCode == 1 &&
- processResult.stderr.contains("401 Anonymous caller")) {
- error =
- "\n\nYou need to authenticate by running:\npython $gsutilPy config\n";
- }
- throw new Exception(error);
- }
- return processResult.stdout;
+ return processResult.stdout;
+ });
}
/// Returns the contents of the provided cloud storage [path], or null if it