Remote: Async upload (Part 8)
Update UI to display background uploads that are still active after the build with message like:
```
INFO: Build completed successfully, 5001 total actions
Waiting for remote cache: 4911 uploads; 16s
```
Part of https://github.com/bazelbuild/bazel/pull/13655.
PiperOrigin-RevId: 395047830
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionUploadFinishedEvent.java b/src/main/java/com/google/devtools/build/lib/actions/ActionUploadFinishedEvent.java
new file mode 100644
index 0000000..7d602cc
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionUploadFinishedEvent.java
@@ -0,0 +1,35 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.actions;
+
+import com.google.auto.value.AutoValue;
+import com.google.devtools.build.lib.events.ExtendedEventHandler.ProgressLike;
+
+/** The event that is fired when the file being uploaded by the action is finished. */
+@AutoValue
+public abstract class ActionUploadFinishedEvent implements ProgressLike {
+
+ public static ActionUploadFinishedEvent create(
+ ActionExecutionMetadata action, String resourceId) {
+ return new AutoValue_ActionUploadFinishedEvent(action, resourceId);
+ }
+
+ /** Returns the associated action. */
+ public abstract ActionExecutionMetadata action();
+
+ /**
+ * Returns the id that uniquely determines the resource being uploaded among all upload events.
+ */
+ public abstract String resourceId();
+}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionUploadStartedEvent.java b/src/main/java/com/google/devtools/build/lib/actions/ActionUploadStartedEvent.java
new file mode 100644
index 0000000..ac34a24
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionUploadStartedEvent.java
@@ -0,0 +1,33 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.actions;
+
+import com.google.auto.value.AutoValue;
+import com.google.devtools.build.lib.events.ExtendedEventHandler.ProgressLike;
+
+/** The event that is fired when the action is about to upload a file. */
+@AutoValue
+public abstract class ActionUploadStartedEvent implements ProgressLike {
+ public static ActionUploadStartedEvent create(ActionExecutionMetadata action, String resourceId) {
+ return new AutoValue_ActionUploadStartedEvent(action, resourceId);
+ }
+
+ /** Returns the associated action. */
+ public abstract ActionExecutionMetadata action();
+
+ /**
+ * Returns the id that uniquely determines the resource being uploaded among all upload events.
+ */
+ public abstract String resourceId();
+}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
index fced8f1..af68ddf 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
@@ -30,7 +30,11 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
+import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
+import com.google.devtools.build.lib.actions.ActionUploadFinishedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadStartedEvent;
import com.google.devtools.build.lib.concurrent.ThreadSafety;
+import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.exec.SpawnProgressEvent;
import com.google.devtools.build.lib.remote.common.BulkTransferException;
import com.google.devtools.build.lib.remote.common.LazyFileOutputStream;
@@ -62,6 +66,7 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.Nullable;
/**
* A cache for storing artifacts (input and output) as well as the output of running an action.
@@ -80,6 +85,7 @@
private static final ListenableFuture<Void> COMPLETED_SUCCESS = immediateFuture(null);
private static final ListenableFuture<byte[]> EMPTY_BYTES = immediateFuture(new byte[0]);
+ private final ExtendedEventHandler reporter;
private final CountDownLatch closeCountDownLatch = new CountDownLatch(1);
protected final AsyncTaskCache.NoResult<Digest> casUploadCache = AsyncTaskCache.NoResult.create();
@@ -88,7 +94,11 @@
protected final DigestUtil digestUtil;
public RemoteCache(
- RemoteCacheClient cacheProtocol, RemoteOptions options, DigestUtil digestUtil) {
+ ExtendedEventHandler reporter,
+ RemoteCacheClient cacheProtocol,
+ RemoteOptions options,
+ DigestUtil digestUtil) {
+ this.reporter = reporter;
this.cacheProtocol = cacheProtocol;
this.options = options;
this.digestUtil = digestUtil;
@@ -100,6 +110,23 @@
return getFromFuture(cacheProtocol.downloadActionResult(context, actionKey, inlineOutErr));
}
+ private void postUploadStartedEvent(@Nullable ActionExecutionMetadata action, String resourceId) {
+ if (action == null) {
+ return;
+ }
+
+ reporter.post(ActionUploadStartedEvent.create(action, resourceId));
+ }
+
+ private void postUploadFinishedEvent(
+ @Nullable ActionExecutionMetadata action, String resourceId) {
+ if (action == null) {
+ return;
+ }
+
+ reporter.post(ActionUploadFinishedEvent.create(action, resourceId));
+ }
+
/**
* Returns a set of digests that the remote cache does not know about. The returned set is
* guaranteed to be a subset of {@code digests}.
@@ -115,7 +142,37 @@
/** Upload the action result to the remote cache. */
public ListenableFuture<Void> uploadActionResult(
RemoteActionExecutionContext context, ActionKey actionKey, ActionResult actionResult) {
- return cacheProtocol.uploadActionResult(context, actionKey, actionResult);
+
+ ActionExecutionMetadata action = context.getSpawnOwner();
+
+ Completable upload =
+ Completable.using(
+ () -> {
+ String resourceId = "ac/" + actionKey.getDigest().getHash();
+ postUploadStartedEvent(action, resourceId);
+ return resourceId;
+ },
+ resourceId ->
+ RxFutures.toCompletable(
+ () -> cacheProtocol.uploadActionResult(context, actionKey, actionResult),
+ directExecutor()),
+ resourceId -> postUploadFinishedEvent(action, resourceId));
+
+ return RxFutures.toListenableFuture(upload);
+ }
+
+ private Completable doUploadFile(RemoteActionExecutionContext context, Digest digest, Path file) {
+ ActionExecutionMetadata action = context.getSpawnOwner();
+ return Completable.using(
+ () -> {
+ String resourceId = "cas/" + digest.getHash();
+ postUploadStartedEvent(action, resourceId);
+ return resourceId;
+ },
+ resourceId ->
+ RxFutures.toCompletable(
+ () -> cacheProtocol.uploadFile(context, digest, file), directExecutor()),
+ resourceId -> postUploadFinishedEvent(action, resourceId));
}
/**
@@ -134,14 +191,26 @@
return COMPLETED_SUCCESS;
}
- Completable upload =
- casUploadCache.executeIfNot(
- digest,
- RxFutures.toCompletable(
- () -> cacheProtocol.uploadFile(context, digest, file), directExecutor()));
+ Completable upload = casUploadCache.executeIfNot(digest, doUploadFile(context, digest, file));
+
return RxFutures.toListenableFuture(upload);
}
+ private Completable doUploadBlob(
+ RemoteActionExecutionContext context, Digest digest, ByteString data) {
+ ActionExecutionMetadata action = context.getSpawnOwner();
+ return Completable.using(
+ () -> {
+ String resourceId = "cas/" + digest.getHash();
+ postUploadStartedEvent(action, resourceId);
+ return resourceId;
+ },
+ resourceId ->
+ RxFutures.toCompletable(
+ () -> cacheProtocol.uploadBlob(context, digest, data), directExecutor()),
+ resourceId -> postUploadFinishedEvent(action, resourceId));
+ }
+
/**
* Upload sequence of bytes to the remote cache.
*
@@ -158,11 +227,8 @@
return COMPLETED_SUCCESS;
}
- Completable upload =
- casUploadCache.executeIfNot(
- digest,
- RxFutures.toCompletable(
- () -> cacheProtocol.uploadBlob(context, digest, data), directExecutor()));
+ Completable upload = casUploadCache.executeIfNot(digest, doUploadBlob(context, digest, data));
+
return RxFutures.toListenableFuture(upload);
}
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionCache.java
index 4a4135c..86ec811 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionCache.java
@@ -22,6 +22,7 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
import com.google.devtools.build.lib.remote.common.RemoteCacheClient;
import com.google.devtools.build.lib.remote.merkletree.MerkleTree;
@@ -42,8 +43,11 @@
public class RemoteExecutionCache extends RemoteCache {
public RemoteExecutionCache(
- RemoteCacheClient protocolImpl, RemoteOptions options, DigestUtil digestUtil) {
- super(protocolImpl, options, digestUtil);
+ ExtendedEventHandler reporter,
+ RemoteCacheClient protocolImpl,
+ RemoteOptions options,
+ DigestUtil digestUtil) {
+ super(reporter, protocolImpl, options, digestUtil);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
index b5b0e7e..7b549dd 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
@@ -65,6 +65,7 @@
import com.google.common.eventbus.Subscribe;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
@@ -262,6 +263,11 @@
return remoteActionExecutionContext;
}
+ /** Returns the {@link ActionExecutionMetadata} that owns this action. */
+ public ActionExecutionMetadata getOwner() {
+ return spawn.getResourceOwner();
+ }
+
/**
* Returns the sum of file sizes plus protobuf sizes used to represent the inputs of this
* action.
@@ -381,7 +387,7 @@
TracingMetadataUtils.buildMetadata(
buildRequestId, commandId, actionKey.getDigest().getHash(), spawn.getResourceOwner());
RemoteActionExecutionContext remoteActionExecutionContext =
- RemoteActionExecutionContext.createForSpawn(spawn, metadata);
+ RemoteActionExecutionContext.create(spawn, metadata);
return new RemoteAction(
spawn,
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
index db52003..289d92c 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
@@ -220,7 +220,8 @@
handleInitFailure(env, e, Code.CACHE_INIT_FAILURE);
return;
}
- RemoteCache remoteCache = new RemoteCache(cacheClient, remoteOptions, digestUtil);
+ RemoteCache remoteCache =
+ new RemoteCache(env.getReporter(), cacheClient, remoteOptions, digestUtil);
actionContextProvider =
RemoteActionContextProvider.createForRemoteCaching(
env, remoteCache, /* retryScheduler= */ null, digestUtil);
@@ -543,7 +544,7 @@
}
execChannel.release();
RemoteExecutionCache remoteCache =
- new RemoteExecutionCache(cacheClient, remoteOptions, digestUtil);
+ new RemoteExecutionCache(env.getReporter(), cacheClient, remoteOptions, digestUtil);
actionContextProvider =
RemoteActionContextProvider.createForRemoteExecution(
env, remoteCache, remoteExecutor, retryScheduler, digestUtil, logDir);
@@ -573,7 +574,8 @@
}
}
- RemoteCache remoteCache = new RemoteCache(cacheClient, remoteOptions, digestUtil);
+ RemoteCache remoteCache =
+ new RemoteCache(env.getReporter(), cacheClient, remoteOptions, digestUtil);
actionContextProvider =
RemoteActionContextProvider.createForRemoteCaching(
env, remoteCache, retryScheduler, digestUtil);
diff --git a/src/main/java/com/google/devtools/build/lib/remote/common/RemoteActionExecutionContext.java b/src/main/java/com/google/devtools/build/lib/remote/common/RemoteActionExecutionContext.java
index e1616f3..52fafe3 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/common/RemoteActionExecutionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/common/RemoteActionExecutionContext.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.remote.common;
import build.bazel.remote.execution.v2.RequestMetadata;
+import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
import com.google.devtools.build.lib.actions.Spawn;
import javax.annotation.Nullable;
@@ -33,6 +34,16 @@
*/
NetworkTime getNetworkTime();
+ @Nullable
+ default ActionExecutionMetadata getSpawnOwner() {
+ Spawn spawn = getSpawn();
+ if (spawn == null) {
+ return null;
+ }
+
+ return spawn.getResourceOwner();
+ }
+
/** Creates a {@link SimpleRemoteActionExecutionContext} with given {@link RequestMetadata}. */
static RemoteActionExecutionContext create(RequestMetadata metadata) {
return new SimpleRemoteActionExecutionContext(/*spawn=*/ null, metadata, new NetworkTime());
@@ -42,7 +53,7 @@
* Creates a {@link SimpleRemoteActionExecutionContext} with given {@link Spawn} and {@link
* RequestMetadata}.
*/
- static RemoteActionExecutionContext createForSpawn(Spawn spawn, RequestMetadata metadata) {
+ static RemoteActionExecutionContext create(@Nullable Spawn spawn, RequestMetadata metadata) {
return new SimpleRemoteActionExecutionContext(spawn, metadata, new NetworkTime());
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java
index ae84347..cbcb93e 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/UiEventHandler.java
@@ -23,6 +23,8 @@
import com.google.devtools.build.lib.actions.ActionProgressEvent;
import com.google.devtools.build.lib.actions.ActionScanningCompletedEvent;
import com.google.devtools.build.lib.actions.ActionStartedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadFinishedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadStartedEvent;
import com.google.devtools.build.lib.actions.CachingActionEvent;
import com.google.devtools.build.lib.actions.RunningActionEvent;
import com.google.devtools.build.lib.actions.ScanningActionEvent;
@@ -575,9 +577,8 @@
ignoreRefreshLimitOnce();
refresh();
- // After a build has completed, only stop updating the UI if there is no more BEP
- // upload happening.
- if (stateTracker.pendingTransports() == 0) {
+ // After a build has completed, only stop updating the UI if there is no more activities.
+ if (!stateTracker.hasActivities()) {
buildRunning = false;
done = true;
}
@@ -706,7 +707,7 @@
@AllowConcurrentEvents
public void actionProgress(ActionProgressEvent event) {
stateTracker.actionProgress(event);
- refresh();
+ refreshSoon();
}
@Subscribe
@@ -723,6 +724,30 @@
refreshSoon();
}
+ private void checkActivities() {
+ if (stateTracker.hasActivities()) {
+ refreshSoon();
+ } else {
+ stopUpdateThread();
+ flushStdOutStdErrBuffers();
+ ignoreRefreshLimitOnce();
+ refresh();
+ }
+ }
+
+ @Subscribe
+ @AllowConcurrentEvents
+ public void actionUploadStarted(ActionUploadStartedEvent event) {
+ stateTracker.actionUploadStarted(event);
+ refreshSoon();
+ }
+
+ @Subscribe
+ public void actionUploadFinished(ActionUploadFinishedEvent event) {
+ stateTracker.actionUploadFinished(event);
+ checkActivities();
+ }
+
@Subscribe
public void testFilteringComplete(TestFilteringCompleteEvent event) {
stateTracker.testFilteringComplete(event);
@@ -797,12 +822,7 @@
this.handle(Event.info(null, "Transport " + event.transport().name() + " closed"));
}
- if (stateTracker.pendingTransports() == 0) {
- stopUpdateThread();
- flushStdOutStdErrBuffers();
- ignoreRefreshLimitOnce();
- }
- refresh();
+ checkActivities();
}
private void refresh() {
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java b/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java
index eef7ea5..84ff8f1 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/UiStateTracker.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.lib.runtime;
import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Comparators;
@@ -28,6 +27,8 @@
import com.google.devtools.build.lib.actions.ActionProgressEvent;
import com.google.devtools.build.lib.actions.ActionScanningCompletedEvent;
import com.google.devtools.build.lib.actions.ActionStartedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadFinishedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadStartedEvent;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CachingActionEvent;
import com.google.devtools.build.lib.actions.RunningActionEvent;
@@ -55,6 +56,8 @@
import com.google.devtools.build.lib.util.io.PositionAwareAnsiTerminalWriter;
import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus;
import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Deque;
@@ -360,6 +363,8 @@
}
private final Map<Artifact, ActionState> activeActions;
+ private final AtomicInteger activeActionUploads = new AtomicInteger(0);
+ private final AtomicInteger activeActionDownloads = new AtomicInteger(0);
// running downloads are identified by the original URL they were trying to access.
private final Deque<String> runningDownloads;
@@ -392,7 +397,7 @@
// Set of build event protocol transports that need yet to be closed.
private final Set<BuildEventTransport> bepOpenTransports = new HashSet<>();
// The point in time when closing of BEP transports was started.
- private long bepTransportClosingStartTimeMillis;
+ private Instant buildCompleteAt;
UiStateTracker(Clock clock, int targetWidth) {
this.activeActions = new ConcurrentHashMap<>();
@@ -472,8 +477,7 @@
void buildComplete(BuildCompleteEvent event) {
buildComplete = true;
- // Build event protocol transports are closed right after the build complete event.
- bepTransportClosingStartTimeMillis = clock.currentTimeMillis();
+ buildCompleteAt = Instant.ofEpochMilli(clock.currentTimeMillis());
if (event.getResult().getSuccess()) {
status = "INFO";
@@ -623,6 +627,14 @@
}
}
+ void actionUploadStarted(ActionUploadStartedEvent event) {
+ activeActionUploads.incrementAndGet();
+ }
+
+ void actionUploadFinished(ActionUploadFinishedEvent event) {
+ activeActionUploads.decrementAndGet();
+ }
+
/** From a string, take a suffix of at most the given length. */
static String suffix(String s, int len) {
if (len <= 0) {
@@ -990,8 +1002,11 @@
bepOpenTransports.remove(event.transport());
}
- synchronized int pendingTransports() {
- return bepOpenTransports.size();
+ synchronized boolean hasActivities() {
+ return !(buildComplete
+ && bepOpenTransports.isEmpty()
+ && activeActionUploads.get() == 0
+ && activeActionDownloads.get() == 0);
}
/**
@@ -1006,7 +1021,7 @@
if (runningDownloads.size() >= 1) {
return true;
}
- if (buildComplete && !bepOpenTransports.isEmpty()) {
+ if (buildComplete && hasActivities()) {
return true;
}
if (status != null) {
@@ -1129,6 +1144,55 @@
}
/**
+ * Display any action uploads/downloads that are still active after the build. Most likely,
+ * because upload/download takes longer than the build itself.
+ */
+ private void maybeReportActiveUploadsOrDownloads(PositionAwareAnsiTerminalWriter terminalWriter)
+ throws IOException {
+ int uploads = activeActionUploads.get();
+ int downloads = activeActionDownloads.get();
+
+ if (!buildComplete || (uploads == 0 && downloads == 0)) {
+ return;
+ }
+
+ Duration waitTime =
+ Duration.between(buildCompleteAt, Instant.ofEpochMilli(clock.currentTimeMillis()));
+ if (waitTime.getSeconds() == 0) {
+ // Special case for when bazel was interrupted, in which case we don't want to have a message.
+ return;
+ }
+
+ String suffix = "";
+ if (waitTime.compareTo(Duration.ofSeconds(SHOW_TIME_THRESHOLD_SECONDS)) > 0) {
+ suffix = "; " + waitTime.getSeconds() + "s";
+ }
+
+ String message = "Waiting for remote cache: ";
+ if (uploads != 0) {
+ if (uploads == 1) {
+ message += "1 upload";
+ } else {
+ message += uploads + " uploads";
+ }
+ }
+
+ if (downloads != 0) {
+ if (uploads != 0) {
+ message += ", ";
+ }
+
+ if (downloads == 1) {
+ message += "1 download";
+ } else {
+ message += downloads + " downloads";
+ }
+ }
+
+ terminalWriter.newline().append(message).append(suffix);
+ }
+
+ /**
* Display any BEP transports that are still open after the build. Most likely, because uploading
* build events takes longer than the build itself.
*/
@@ -1137,9 +1201,9 @@
if (!buildComplete || bepOpenTransports.isEmpty()) {
return;
}
- long sinceSeconds =
- MILLISECONDS.toSeconds(clock.currentTimeMillis() - bepTransportClosingStartTimeMillis);
- if (sinceSeconds == 0) {
+ Duration waitTime =
+ Duration.between(buildCompleteAt, Instant.ofEpochMilli(clock.currentTimeMillis()));
+ if (waitTime.getSeconds() == 0) {
// Special case for when bazel was interrupted, in which case we don't want to have
// a BEP upload message.
return;
@@ -1150,17 +1214,17 @@
String waitMessage = "Waiting for build events upload: ";
String name = bepOpenTransports.iterator().next().name();
- String line = waitMessage + name + " " + sinceSeconds + "s";
+ String line = waitMessage + name + " " + waitTime.getSeconds() + "s";
if (count == 1 && line.length() <= maxWidth) {
terminalWriter.newline().append(line);
} else if (count == 1) {
waitMessage = "Waiting for: ";
- String waitSecs = " " + sinceSeconds + "s";
+ String waitSecs = " " + waitTime.getSeconds() + "s";
int maxNameWidth = maxWidth - waitMessage.length() - waitSecs.length();
terminalWriter.newline().append(waitMessage + shortenedString(name, maxNameWidth) + waitSecs);
} else {
- terminalWriter.newline().append(waitMessage + sinceSeconds + "s");
+ terminalWriter.newline().append(waitMessage + waitTime.getSeconds() + "s");
for (BuildEventTransport transport : bepOpenTransports) {
name = " " + transport.name();
terminalWriter.newline().append(shortenedString(name, maxWidth));
@@ -1197,6 +1261,7 @@
}
if (!shortVersion) {
reportOnDownloads(terminalWriter);
+ maybeReportActiveUploadsOrDownloads(terminalWriter);
maybeReportBepTransports(terminalWriter);
}
return;
@@ -1269,6 +1334,7 @@
}
if (!shortVersion) {
reportOnDownloads(terminalWriter);
+ maybeReportActiveUploadsOrDownloads(terminalWriter);
maybeReportBepTransports(terminalWriter);
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java
index b719889..06a35ad 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java
@@ -51,6 +51,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
+import com.google.common.eventbus.EventBus;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.devtools.build.lib.actions.ActionInputHelper;
@@ -60,6 +61,7 @@
import com.google.devtools.build.lib.authandtls.CallCredentialsProvider;
import com.google.devtools.build.lib.authandtls.GoogleAuthUtils;
import com.google.devtools.build.lib.clock.JavaClock;
+import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.remote.RemoteRetrier.ExponentialBackoff;
import com.google.devtools.build.lib.remote.Retrier.Backoff;
import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
@@ -130,6 +132,7 @@
private Path execRoot;
private FileOutErr outErr;
private FakeActionInputFileCache fakeFileCache;
+ private final Reporter reporter = new Reporter(new EventBus());
private final MutableHandlerRegistry serviceRegistry = new MutableHandlerRegistry();
private final String fakeServerName = "fake server for " + getClass();
private Server fakeServer;
@@ -268,7 +271,7 @@
public void testVirtualActionInputSupport() throws Exception {
RemoteOptions options = Options.getDefaults(RemoteOptions.class);
RemoteExecutionCache client =
- new RemoteExecutionCache(newClient(options), options, DIGEST_UTIL);
+ new RemoteExecutionCache(reporter, newClient(options), options, DIGEST_UTIL);
PathFragment execPath = PathFragment.create("my/exec/path");
VirtualActionInput virtualActionInput =
ActionsTestUtil.createVirtualActionInput(execPath, "hello");
@@ -378,7 +381,7 @@
// arrange
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
Digest fooDigest = DIGEST_UTIL.computeAsUtf8("foo-contents");
Digest barDigest = DIGEST_UTIL.computeAsUtf8("bar-contents");
@@ -401,7 +404,7 @@
public void testUploadDirectory() throws Exception {
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
final Digest fooDigest =
fakeFileCache.createScratchInput(ActionInputHelper.fromPath("a/foo"), "xyz");
@@ -459,7 +462,7 @@
public void testUploadDirectoryEmpty() throws Exception {
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
final Digest barDigest =
fakeFileCache.createScratchInputDirectory(
@@ -498,7 +501,7 @@
public void testUploadDirectoryNested() throws Exception {
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
final Digest wobbleDigest =
fakeFileCache.createScratchInput(ActionInputHelper.fromPath("bar/test/wobble"), "xyz");
@@ -649,7 +652,7 @@
serviceRegistry.addService(ServerInterceptors.intercept(actionCache, interceptor));
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
remoteCache.downloadActionResult(
context,
DIGEST_UTIL.asActionKey(DIGEST_UTIL.computeAsUtf8("key")),
@@ -660,7 +663,7 @@
public void testUpload() throws Exception {
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
final Digest fooDigest =
fakeFileCache.createScratchInput(ActionInputHelper.fromPath("a/foo"), "xyz");
@@ -730,7 +733,7 @@
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
remoteOptions.maxOutboundMessageSize = 80; // Enough for one digest, but not two.
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
final Digest fooDigest =
fakeFileCache.createScratchInput(ActionInputHelper.fromPath("a/foo"), "xyz");
@@ -789,7 +792,7 @@
public void testUploadCacheMissesWithRetries() throws Exception {
RemoteOptions remoteOptions = Options.getDefaults(RemoteOptions.class);
GrpcCacheClient client = newClient(remoteOptions);
- RemoteCache remoteCache = new RemoteCache(client, remoteOptions, DIGEST_UTIL);
+ RemoteCache remoteCache = new RemoteCache(reporter, client, remoteOptions, DIGEST_UTIL);
final Digest fooDigest =
fakeFileCache.createScratchInput(ActionInputHelper.fromPath("a/foo"), "xyz");
diff --git a/src/test/java/com/google/devtools/build/lib/remote/InMemoryRemoteCache.java b/src/test/java/com/google/devtools/build/lib/remote/InMemoryRemoteCache.java
index 5799219..9dcaf36 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/InMemoryRemoteCache.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/InMemoryRemoteCache.java
@@ -16,6 +16,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import build.bazel.remote.execution.v2.Digest;
+import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
import com.google.devtools.build.lib.remote.options.RemoteOptions;
import com.google.devtools.build.lib.remote.util.DigestUtil;
@@ -29,12 +30,15 @@
class InMemoryRemoteCache extends RemoteExecutionCache {
InMemoryRemoteCache(
- Map<Digest, byte[]> casEntries, RemoteOptions options, DigestUtil digestUtil) {
- super(new InMemoryCacheClient(casEntries), options, digestUtil);
+ Reporter reporter,
+ Map<Digest, byte[]> casEntries,
+ RemoteOptions options,
+ DigestUtil digestUtil) {
+ super(reporter, new InMemoryCacheClient(casEntries), options, digestUtil);
}
- InMemoryRemoteCache(RemoteOptions options, DigestUtil digestUtil) {
- super(new InMemoryCacheClient(), options, digestUtil);
+ InMemoryRemoteCache(Reporter reporter, RemoteOptions options, DigestUtil digestUtil) {
+ super(reporter, new InMemoryCacheClient(), options, digestUtil);
}
Digest addContents(RemoteActionExecutionContext context, String txt)
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java
index 9d9dcfe..60e273a 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java
@@ -23,6 +23,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.common.eventbus.EventBus;
import com.google.common.hash.HashCode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
@@ -36,6 +37,7 @@
import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.clock.JavaClock;
+import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.remote.common.BulkTransferException;
import com.google.devtools.build.lib.remote.options.RemoteOptions;
import com.google.devtools.build.lib.remote.util.DigestUtil;
@@ -65,6 +67,7 @@
private static final DigestHashFunction HASH_FUNCTION = DigestHashFunction.SHA256;
+ private final Reporter reporter = new Reporter(new EventBus());
private Path execRoot;
private ArtifactRoot artifactRoot;
private RemoteOptions options;
@@ -385,13 +388,14 @@
return a;
}
- private static RemoteCache newCache(
+ private RemoteCache newCache(
RemoteOptions options, DigestUtil digestUtil, Map<Digest, ByteString> cacheEntries) {
Map<Digest, byte[]> cacheEntriesByteArray =
Maps.newHashMapWithExpectedSize(cacheEntries.size());
for (Map.Entry<Digest, ByteString> entry : cacheEntries.entrySet()) {
cacheEntriesByteArray.put(entry.getKey(), entry.getValue().toByteArray());
}
- return new RemoteCache(new InMemoryCacheClient(cacheEntriesByteArray), options, digestUtil);
+ return new RemoteCache(
+ reporter, new InMemoryCacheClient(cacheEntriesByteArray), options, digestUtil);
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTest.java
similarity index 66%
rename from src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
rename to src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTest.java
index 054a730..cec16dc 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTest.java
@@ -16,17 +16,32 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.remote.util.Utils.getFromFuture;
+import build.bazel.remote.execution.v2.Action;
import build.bazel.remote.execution.v2.ActionResult;
import build.bazel.remote.execution.v2.Digest;
import build.bazel.remote.execution.v2.RequestMetadata;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.eventbus.EventBus;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.devtools.build.lib.actions.ActionInputHelper;
+import com.google.devtools.build.lib.actions.ActionUploadFinishedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadStartedEvent;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
+import com.google.devtools.build.lib.actions.ResourceSet;
+import com.google.devtools.build.lib.actions.SimpleSpawn;
+import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.clock.JavaClock;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.events.StoredEventHandler;
+import com.google.devtools.build.lib.exec.util.FakeOwner;
import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
+import com.google.devtools.build.lib.remote.common.RemoteCacheClient.ActionKey;
import com.google.devtools.build.lib.remote.options.RemoteOptions;
import com.google.devtools.build.lib.remote.util.DigestUtil;
import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
@@ -54,8 +69,10 @@
/** Tests for {@link RemoteCache}. */
@RunWith(JUnit4.class)
-public class RemoteCacheTests {
+public class RemoteCacheTest {
+ private final Reporter reporter = new Reporter(new EventBus());
+ private final StoredEventHandler eventHandler = new StoredEventHandler();
private RemoteActionExecutionContext context;
private FileSystem fs;
private Path execRoot;
@@ -67,10 +84,21 @@
@Before
public void setUp() throws Exception {
+ reporter.addHandler(eventHandler);
+
MockitoAnnotations.initMocks(this);
RequestMetadata metadata =
TracingMetadataUtils.buildMetadata("none", "none", "action-id", null);
- context = RemoteActionExecutionContext.create(metadata);
+ Spawn spawn =
+ new SimpleSpawn(
+ new FakeOwner("foo", "bar", "//dummy:label"),
+ /*arguments=*/ ImmutableList.of(),
+ /*environment=*/ ImmutableMap.of(),
+ /*executionInfo=*/ ImmutableMap.of(),
+ /*inputs=*/ NestedSetBuilder.emptySet(Order.STABLE_ORDER),
+ /*outputs=*/ ImmutableSet.of(),
+ ResourceSet.ZERO);
+ context = RemoteActionExecutionContext.create(spawn, metadata);
fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
execRoot = fs.getPath("/execroot/main");
execRoot.createDirectoryAndParents();
@@ -142,7 +170,7 @@
Path file = fs.getPath("/execroot/symlink-to-file");
RemoteOptions options = Options.getDefaults(RemoteOptions.class);
options.remoteDownloadSymlinkTemplate = "/home/alice/cas/{hash}-{size_bytes}";
- RemoteCache remoteCache = new InMemoryRemoteCache(cas, options, digestUtil);
+ RemoteCache remoteCache = new InMemoryRemoteCache(reporter, cas, options, digestUtil);
// act
getFromFuture(remoteCache.downloadFile(context, file, helloDigest));
@@ -171,8 +199,53 @@
.containsExactly(emptyDigest);
}
+ @Test
+ public void uploadActionResult_firesUploadEvents() throws Exception {
+ InMemoryRemoteCache remoteCache = newRemoteCache();
+ ActionKey actionKey = new ActionKey(digestUtil.compute(Action.getDefaultInstance()));
+ ActionResult actionResult = ActionResult.getDefaultInstance();
+
+ getFromFuture(remoteCache.uploadActionResult(context, actionKey, actionResult));
+
+ String resourceId = "ac/" + actionKey.getDigest().getHash();
+ assertThat(eventHandler.getPosts())
+ .containsExactly(
+ ActionUploadStartedEvent.create(context.getSpawn().getResourceOwner(), resourceId),
+ ActionUploadFinishedEvent.create(context.getSpawn().getResourceOwner(), resourceId));
+ }
+
+ @Test
+ public void uploadBlob_firesUploadEvents() throws Exception {
+ InMemoryRemoteCache remoteCache = newRemoteCache();
+ ByteString content = ByteString.copyFromUtf8("content");
+ Digest digest = digestUtil.compute(content.toByteArray());
+
+ getFromFuture(remoteCache.uploadBlob(context, digest, content));
+
+ String resourceId = "cas/" + digest.getHash();
+ assertThat(eventHandler.getPosts())
+ .containsExactly(
+ ActionUploadStartedEvent.create(context.getSpawn().getResourceOwner(), resourceId),
+ ActionUploadFinishedEvent.create(context.getSpawn().getResourceOwner(), resourceId));
+ }
+
+ @Test
+ public void uploadFile_firesUploadEvents() throws Exception {
+ InMemoryRemoteCache remoteCache = newRemoteCache();
+ Digest digest = fakeFileCache.createScratchInput(ActionInputHelper.fromPath("file"), "content");
+ Path file = execRoot.getRelative("file");
+
+ getFromFuture(remoteCache.uploadFile(context, digest, file));
+
+ String resourceId = "cas/" + digest.getHash();
+ assertThat(eventHandler.getPosts())
+ .containsExactly(
+ ActionUploadStartedEvent.create(context.getSpawn().getResourceOwner(), resourceId),
+ ActionUploadFinishedEvent.create(context.getSpawn().getResourceOwner(), resourceId));
+ }
+
private InMemoryRemoteCache newRemoteCache() {
RemoteOptions options = Options.getDefaults(RemoteOptions.class);
- return new InMemoryRemoteCache(options, digestUtil);
+ return new InMemoryRemoteCache(reporter, options, digestUtil);
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java
index b375467..d83c648 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteExecutionServiceTest.java
@@ -153,7 +153,7 @@
checkNotNull(stderr.getParentDirectory()).createDirectoryAndParents();
outErr = new FileOutErr(stdout, stderr);
- cache = spy(new InMemoryRemoteCache(remoteOptions, digestUtil));
+ cache = spy(new InMemoryRemoteCache(reporter, remoteOptions, digestUtil));
executor = mock(RemoteExecutionClient.class);
RequestMetadata metadata =
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerWithGrpcRemoteExecutorTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerWithGrpcRemoteExecutorTest.java
index f68274a..61b20a8 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerWithGrpcRemoteExecutorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerWithGrpcRemoteExecutorTest.java
@@ -298,7 +298,7 @@
DIGEST_UTIL,
uploader);
RemoteExecutionCache remoteCache =
- new RemoteExecutionCache(cacheProtocol, remoteOptions, DIGEST_UTIL);
+ new RemoteExecutionCache(reporter, cacheProtocol, remoteOptions, DIGEST_UTIL);
RemoteExecutionService remoteExecutionService =
new RemoteExecutionService(
reporter,
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java b/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java
index 197ac41..37c1473 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java
@@ -31,6 +31,8 @@
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.ActionProgressEvent;
import com.google.devtools.build.lib.actions.ActionStartedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadFinishedEvent;
+import com.google.devtools.build.lib.actions.ActionUploadStartedEvent;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.actions.BuildConfigurationEvent;
@@ -1408,4 +1410,59 @@
return false;
}
}
+
+ @Test
+ public void waitingRemoteCacheMessage_beforeBuildComplete_invisible() throws IOException {
+ ManualClock clock = new ManualClock();
+ Action action = mockAction("Some action", "foo");
+ UiStateTracker stateTracker = new UiStateTracker(clock);
+ stateTracker.actionUploadStarted(ActionUploadStartedEvent.create(action, "foo"));
+ LoggingTerminalWriter terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true);
+
+ stateTracker.writeProgressBar(terminalWriter);
+
+ String output = terminalWriter.getTranscript();
+ assertThat(output).doesNotContain("1 upload");
+ }
+
+ @Test
+ public void waitingRemoteCacheMessage_afterBuildComplete_visible() throws IOException {
+ ManualClock clock = new ManualClock();
+ Action action = mockAction("Some action", "foo");
+ UiStateTracker stateTracker = new UiStateTracker(clock);
+ stateTracker.actionUploadStarted(ActionUploadStartedEvent.create(action, "foo"));
+ BuildResult buildResult = new BuildResult(clock.currentTimeMillis());
+ buildResult.setDetailedExitCode(DetailedExitCode.success());
+ buildResult.setStopTime(clock.currentTimeMillis());
+ stateTracker.buildComplete(new BuildCompleteEvent(buildResult));
+ clock.advanceMillis(Duration.ofSeconds(2).toMillis());
+ LoggingTerminalWriter terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true);
+
+ stateTracker.writeProgressBar(terminalWriter);
+
+ String output = terminalWriter.getTranscript();
+ assertThat(output).contains("1 upload");
+ }
+
+ @Test
+ public void waitingRemoteCacheMessage_multipleUploadEvents_countCorrectly() throws IOException {
+ ManualClock clock = new ManualClock();
+ Action action = mockAction("Some action", "foo");
+ UiStateTracker stateTracker = new UiStateTracker(clock);
+ stateTracker.actionUploadStarted(ActionUploadStartedEvent.create(action, "a"));
+ BuildResult buildResult = new BuildResult(clock.currentTimeMillis());
+ buildResult.setDetailedExitCode(DetailedExitCode.success());
+ buildResult.setStopTime(clock.currentTimeMillis());
+ stateTracker.buildComplete(new BuildCompleteEvent(buildResult));
+ stateTracker.actionUploadStarted(ActionUploadStartedEvent.create(action, "b"));
+ stateTracker.actionUploadStarted(ActionUploadStartedEvent.create(action, "c"));
+ stateTracker.actionUploadFinished(ActionUploadFinishedEvent.create(action, "a"));
+ clock.advanceMillis(Duration.ofSeconds(2).toMillis());
+ LoggingTerminalWriter terminalWriter = new LoggingTerminalWriter(/*discardHighlight=*/ true);
+
+ stateTracker.writeProgressBar(terminalWriter);
+
+ String output = terminalWriter.getTranscript();
+ assertThat(output).contains("2 uploads");
+ }
}
diff --git a/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/BUILD b/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/BUILD
index a354c12..ac718a9 100644
--- a/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/BUILD
+++ b/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/BUILD
@@ -20,6 +20,7 @@
deps = [
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/actions:localhost_capacity",
+ "//src/main/java/com/google/devtools/build/lib/events",
"//src/main/java/com/google/devtools/build/lib/remote",
"//src/main/java/com/google/devtools/build/lib/remote/common",
"//src/main/java/com/google/devtools/build/lib/remote/disk",
diff --git a/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/OnDiskBlobStoreCache.java b/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/OnDiskBlobStoreCache.java
index cbf1586..fc81d43 100644
--- a/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/OnDiskBlobStoreCache.java
+++ b/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/OnDiskBlobStoreCache.java
@@ -19,6 +19,8 @@
import build.bazel.remote.execution.v2.Directory;
import build.bazel.remote.execution.v2.DirectoryNode;
import build.bazel.remote.execution.v2.FileNode;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.remote.RemoteCache;
import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
import com.google.devtools.build.lib.remote.disk.DiskCacheClient;
@@ -32,6 +34,17 @@
public OnDiskBlobStoreCache(RemoteOptions options, Path cacheDir, DigestUtil digestUtil) {
super(
+ new ExtendedEventHandler() {
+ @Override
+ public void post(Postable obj) {
+ // do nothing
+ }
+
+ @Override
+ public void handle(Event event) {
+ // do nothing
+ }
+ },
new DiskCacheClient(cacheDir, /* verifyDownloads= */ true, digestUtil),
options,
digestUtil);