Remote: Use parameters instead of thread-local storage to provide tracing metadata. (Part 4)

Change RemoteCacheClient#upload{File,Blob} to use RemoteActionExecutionContext.

PiperOrigin-RevId: 354472775
diff --git a/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploader.java b/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploader.java
index 1b2fd7c..0ca33b6 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploader.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploader.java
@@ -14,6 +14,7 @@
 package com.google.devtools.build.lib.remote;
 
 import build.bazel.remote.execution.v2.Digest;
+import build.bazel.remote.execution.v2.RequestMetadata;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
@@ -29,7 +30,11 @@
 import com.google.devtools.build.lib.buildeventstream.PathConverter;
 import com.google.devtools.build.lib.collect.ImmutableIterable;
 import com.google.devtools.build.lib.remote.common.MissingDigestsFinder;
+import com.google.devtools.build.lib.remote.common.NetworkTime;
+import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
+import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContextImpl;
 import com.google.devtools.build.lib.remote.util.DigestUtil;
+import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
 import com.google.devtools.build.lib.vfs.Path;
 import io.grpc.Context;
 import io.netty.util.AbstractReferenceCounted;
@@ -50,7 +55,8 @@
     implements BuildEventArtifactUploader {
 
   private final ListeningExecutorService uploadExecutor;
-  private final Context ctx;
+  private final String buildRequestId;
+  private final String commandId;
   private final ByteStreamUploader uploader;
   private final String remoteServerInstanceName;
   private final MissingDigestsFinder missingDigestsFinder;
@@ -61,7 +67,8 @@
       ByteStreamUploader uploader,
       MissingDigestsFinder missingDigestsFinder,
       String remoteServerName,
-      Context ctx,
+      String buildRequestId,
+      String commandId,
       @Nullable String remoteInstanceName,
       int maxUploadThreads) {
     this.uploader = Preconditions.checkNotNull(uploader);
@@ -69,7 +76,8 @@
     if (!Strings.isNullOrEmpty(remoteInstanceName)) {
       remoteServerInstanceName += "/" + remoteInstanceName;
     }
-    this.ctx = ctx;
+    this.buildRequestId = buildRequestId;
+    this.commandId = commandId;
     this.remoteServerInstanceName = remoteServerInstanceName;
     // Limit the maximum threads number to 1000 (chosen arbitrarily)
     this.uploadExecutor =
@@ -153,6 +161,8 @@
    */
   private ListenableFuture<ImmutableIterable<PathMetadata>> queryRemoteCache(
       ImmutableList<ListenableFuture<PathMetadata>> allPaths) throws Exception {
+    Context ctx = TracingMetadataUtils.contextWithMetadata(buildRequestId, commandId, "bes-upload");
+
     List<PathMetadata> knownRemotePaths = new ArrayList<>(allPaths.size());
     List<PathMetadata> filesToQuery = new ArrayList<>();
     Set<Digest> digestsToQuery = new HashSet<>();
@@ -185,6 +195,11 @@
    */
   private ListenableFuture<List<PathMetadata>> uploadLocalFiles(
       ImmutableIterable<PathMetadata> allPaths) {
+    RequestMetadata metadata =
+        TracingMetadataUtils.buildMetadata(buildRequestId, commandId, "bes-upload");
+    RemoteActionExecutionContext context =
+        new RemoteActionExecutionContextImpl(metadata, new NetworkTime());
+
     ImmutableList.Builder<ListenableFuture<PathMetadata>> allPathsUploaded =
         ImmutableList.builder();
     for (PathMetadata path : allPaths) {
@@ -192,12 +207,8 @@
         Chunker chunker =
             Chunker.builder().setInput(path.getDigest().getSizeBytes(), path.getPath()).build();
         final ListenableFuture<Void> upload;
-        Context prevCtx = ctx.attach();
-        try {
-          upload = uploader.uploadBlobAsync(path.getDigest(), chunker, /* forceUpload=*/ false);
-        } finally {
-          ctx.detach(prevCtx);
-        }
+        upload =
+            uploader.uploadBlobAsync(context, path.getDigest(), chunker, /* forceUpload= */ false);
         allPathsUploaded.add(Futures.transform(upload, unused -> path, uploadExecutor));
       } else {
         allPathsUploaded.add(Futures.immediateFuture(path));
diff --git a/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderFactory.java b/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderFactory.java
index 978c10f..1c27fbe 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderFactory.java
@@ -18,7 +18,6 @@
 import com.google.devtools.build.lib.remote.options.RemoteOptions;
 import com.google.devtools.build.lib.runtime.BuildEventArtifactUploaderFactory;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import io.grpc.Context;
 import javax.annotation.Nullable;
 
 /**
@@ -29,7 +28,8 @@
 
   private final ByteStreamUploader uploader;
   private final String remoteServerName;
-  private final Context ctx;
+  private final String buildRequestId;
+  private final String commandId;
   private final MissingDigestsFinder missingDigestsFinder;
   @Nullable private final String remoteInstanceName;
 
@@ -37,12 +37,14 @@
       ByteStreamUploader uploader,
       MissingDigestsFinder missingDigestsFinder,
       String remoteServerName,
-      Context ctx,
+      String buildRequestId,
+      String commandId,
       @Nullable String remoteInstanceName) {
     this.uploader = uploader;
     this.missingDigestsFinder = missingDigestsFinder;
     this.remoteServerName = remoteServerName;
-    this.ctx = ctx;
+    this.buildRequestId = buildRequestId;
+    this.commandId = commandId;
     this.remoteInstanceName = remoteInstanceName;
   }
 
@@ -52,7 +54,8 @@
         uploader.retain(),
         missingDigestsFinder,
         remoteServerName,
-        ctx,
+        buildRequestId,
+        commandId,
         remoteInstanceName,
         env.getOptions().getOptions(RemoteOptions.class).buildEventUploadMaxThreads);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/remote/ByteStreamUploader.java b/src/main/java/com/google/devtools/build/lib/remote/ByteStreamUploader.java
index c113379..bba8593 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/ByteStreamUploader.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/ByteStreamUploader.java
@@ -36,12 +36,12 @@
 import com.google.common.util.concurrent.SettableFuture;
 import com.google.devtools.build.lib.authandtls.CallCredentialsProvider;
 import com.google.devtools.build.lib.remote.RemoteRetrier.ProgressiveBackoff;
+import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
 import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
 import com.google.devtools.build.lib.remote.util.Utils;
 import io.grpc.CallOptions;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
-import io.grpc.Context;
 import io.grpc.Metadata;
 import io.grpc.Status;
 import io.grpc.Status.Code;
@@ -134,9 +134,10 @@
    *     uploaded, if {@code true} the blob is uploaded.
    * @throws IOException when reading of the {@link Chunker}s input source fails
    */
-  public void uploadBlob(HashCode hash, Chunker chunker, boolean forceUpload)
+  public void uploadBlob(
+      RemoteActionExecutionContext context, HashCode hash, Chunker chunker, boolean forceUpload)
       throws IOException, InterruptedException {
-    uploadBlobs(singletonMap(hash, chunker), forceUpload);
+    uploadBlobs(context, singletonMap(hash, chunker), forceUpload);
   }
 
   /**
@@ -156,12 +157,14 @@
    *     uploaded, if {@code true} the blob is uploaded.
    * @throws IOException when reading of the {@link Chunker}s input source or uploading fails
    */
-  public void uploadBlobs(Map<HashCode, Chunker> chunkers, boolean forceUpload)
+  public void uploadBlobs(
+      RemoteActionExecutionContext context, Map<HashCode, Chunker> chunkers, boolean forceUpload)
       throws IOException, InterruptedException {
     List<ListenableFuture<Void>> uploads = new ArrayList<>();
 
     for (Map.Entry<HashCode, Chunker> chunkerEntry : chunkers.entrySet()) {
-      uploads.add(uploadBlobAsync(chunkerEntry.getKey(), chunkerEntry.getValue(), forceUpload));
+      uploads.add(
+          uploadBlobAsync(context, chunkerEntry.getKey(), chunkerEntry.getValue(), forceUpload));
     }
 
     try {
@@ -200,14 +203,17 @@
     }
   }
 
-  /** @deprecated Use {@link #uploadBlobAsync(Digest, Chunker, boolean)} instead. */
+  /**
+   * @deprecated Use {@link #uploadBlobAsync(RemoteActionExecutionContext, Digest, Chunker,
+   *     boolean)} instead.
+   */
   @Deprecated
   @VisibleForTesting
   public ListenableFuture<Void> uploadBlobAsync(
-      HashCode hash, Chunker chunker, boolean forceUpload) {
+      RemoteActionExecutionContext context, HashCode hash, Chunker chunker, boolean forceUpload) {
     Digest digest =
         Digest.newBuilder().setHash(hash.toString()).setSizeBytes(chunker.getSize()).build();
-    return uploadBlobAsync(digest, chunker, forceUpload);
+    return uploadBlobAsync(context, digest, chunker, forceUpload);
   }
 
   /**
@@ -227,7 +233,7 @@
    * @throws IOException when reading of the {@link Chunker}s input source fails
    */
   public ListenableFuture<Void> uploadBlobAsync(
-      Digest digest, Chunker chunker, boolean forceUpload) {
+      RemoteActionExecutionContext context, Digest digest, Chunker chunker, boolean forceUpload) {
     synchronized (lock) {
       checkState(!isShutdown, "Must not call uploadBlobs after shutdown.");
 
@@ -242,7 +248,7 @@
 
       ListenableFuture<Void> uploadResult =
           Futures.transform(
-              startAsyncUpload(digest, chunker),
+              startAsyncUpload(context, digest, chunker),
               (v) -> {
                 synchronized (lock) {
                   uploadedBlobs.add(HashCode.fromString(digest.getHash()));
@@ -294,7 +300,8 @@
   }
 
   /** Starts a file upload and returns a future representing the upload. */
-  private ListenableFuture<Void> startAsyncUpload(Digest digest, Chunker chunker) {
+  private ListenableFuture<Void> startAsyncUpload(
+      RemoteActionExecutionContext context, Digest digest, Chunker chunker) {
     try {
       chunker.reset();
     } catch (IOException e) {
@@ -313,7 +320,13 @@
     String resourceName = buildUploadResourceName(instanceName, uploadId, digest);
     AsyncUpload newUpload =
         new AsyncUpload(
-            channel, callCredentialsProvider, callTimeoutSecs, retrier, resourceName, chunker);
+            context,
+            channel,
+            callCredentialsProvider,
+            callTimeoutSecs,
+            retrier,
+            resourceName,
+            chunker);
     ListenableFuture<Void> currUpload = newUpload.start();
     currUpload.addListener(
         () -> {
@@ -348,6 +361,7 @@
 
   private static class AsyncUpload {
 
+    private final RemoteActionExecutionContext context;
     private final Channel channel;
     private final CallCredentialsProvider callCredentialsProvider;
     private final long callTimeoutSecs;
@@ -358,12 +372,14 @@
     private ClientCall<WriteRequest, WriteResponse> call;
 
     AsyncUpload(
+        RemoteActionExecutionContext context,
         Channel channel,
         CallCredentialsProvider callCredentialsProvider,
         long callTimeoutSecs,
         Retrier retrier,
         String resourceName,
         Chunker chunker) {
+      this.context = context;
       this.channel = channel;
       this.callCredentialsProvider = callCredentialsProvider;
       this.callTimeoutSecs = callTimeoutSecs;
@@ -373,7 +389,6 @@
     }
 
     ListenableFuture<Void> start() {
-      Context ctx = Context.current();
       ProgressiveBackoff progressiveBackoff = new ProgressiveBackoff(retrier::newBackoff);
       AtomicLong committedOffset = new AtomicLong(0);
 
@@ -383,8 +398,7 @@
                   retrier.executeAsync(
                       () -> {
                         if (committedOffset.get() < chunker.getSize()) {
-                          return ctx.call(
-                              () -> callAndQueryOnFailure(committedOffset, progressiveBackoff));
+                          return callAndQueryOnFailure(committedOffset, progressiveBackoff);
                         }
                         return Futures.immediateFuture(null);
                       },
@@ -409,7 +423,8 @@
 
     private ByteStreamFutureStub bsFutureStub() {
       return ByteStreamGrpc.newFutureStub(channel)
-          .withInterceptors(TracingMetadataUtils.attachMetadataFromContextInterceptor())
+          .withInterceptors(
+              TracingMetadataUtils.attachMetadataInterceptor(context.getRequestMetadata()))
           .withCallCredentials(callCredentialsProvider.getCallCredentials())
           .withDeadlineAfter(callTimeoutSecs, SECONDS);
     }
@@ -420,7 +435,7 @@
           call(committedOffset),
           Exception.class,
           (e) -> guardQueryWithSuppression(e, committedOffset, progressiveBackoff),
-          Context.current().fixedContextExecutor(MoreExecutors.directExecutor()));
+          MoreExecutors.directExecutor());
     }
 
     private ListenableFuture<Void> guardQueryWithSuppression(
@@ -584,7 +599,9 @@
               }
             }
           };
-      call.start(callListener, TracingMetadataUtils.headersFromCurrentContext());
+      call.start(
+          callListener,
+          TracingMetadataUtils.headersFromRequestMetadata(context.getRequestMetadata()));
       call.request(1);
       return uploadResult;
     }
diff --git a/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java b/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java
index ef031d7..b2aeb4a 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java
@@ -393,16 +393,22 @@
   }
 
   @Override
-  public ListenableFuture<Void> uploadFile(Digest digest, Path path) {
+  public ListenableFuture<Void> uploadFile(
+      RemoteActionExecutionContext context, Digest digest, Path path) {
     return uploader.uploadBlobAsync(
+        context,
         digest,
         Chunker.builder().setInput(digest.getSizeBytes(), path).build(),
         /* forceUpload= */ true);
   }
 
   @Override
-  public ListenableFuture<Void> uploadBlob(Digest digest, ByteString data) {
+  public ListenableFuture<Void> uploadBlob(
+      RemoteActionExecutionContext context, Digest digest, ByteString data) {
     return uploader.uploadBlobAsync(
-        digest, Chunker.builder().setInput(data.toByteArray()).build(), /* forceUpload= */ true);
+        context,
+        digest,
+        Chunker.builder().setInput(data.toByteArray()).build(),
+        /* forceUpload= */ true);
   }
 }
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 3950814..3496243 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
@@ -139,7 +139,7 @@
       int exitCode)
       throws ExecException, IOException, InterruptedException {
     ActionResult.Builder resultBuilder = ActionResult.newBuilder();
-    uploadOutputs(execRoot, actionKey, action, command, outputs, outErr, resultBuilder);
+    uploadOutputs(context, execRoot, actionKey, action, command, outputs, outErr, resultBuilder);
     resultBuilder.setExitCode(exitCode);
     ActionResult result = resultBuilder.build();
     if (exitCode == 0 && !action.getDoNotCache()) {
@@ -162,6 +162,7 @@
   }
 
   private void uploadOutputs(
+      RemoteActionExecutionContext context,
       Path execRoot,
       ActionKey actionKey,
       Action action,
@@ -192,14 +193,14 @@
     for (Digest digest : digestsToUpload) {
       Path file = digestToFile.get(digest);
       if (file != null) {
-        uploads.add(cacheProtocol.uploadFile(digest, file));
+        uploads.add(cacheProtocol.uploadFile(context, digest, file));
       } else {
         ByteString blob = digestToBlobs.get(digest);
         if (blob == null) {
           String message = "FindMissingBlobs call returned an unknown digest: " + digest;
           throw new IOException(message);
         }
-        uploads.add(cacheProtocol.uploadBlob(digest, blob));
+        uploads.add(cacheProtocol.uploadBlob(context, digest, blob));
       }
     }
 
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 8886025..4398404 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.collect.Iterables;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+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;
 import com.google.devtools.build.lib.remote.merkletree.MerkleTree.PathOrBytes;
@@ -53,7 +54,10 @@
    * However, remote execution uses a cache to store input files, and that may be a separate
    * end-point from the executor itself, so the functionality lives here.
    */
-  public void ensureInputsPresent(MerkleTree merkleTree, Map<Digest, Message> additionalInputs)
+  public void ensureInputsPresent(
+      RemoteActionExecutionContext context,
+      MerkleTree merkleTree,
+      Map<Digest, Message> additionalInputs)
       throws IOException, InterruptedException {
     Iterable<Digest> allDigests =
         Iterables.concat(merkleTree.getAllDigests(), additionalInputs.keySet());
@@ -62,30 +66,33 @@
 
     List<ListenableFuture<Void>> uploadFutures = new ArrayList<>();
     for (Digest missingDigest : missingDigests) {
-      uploadFutures.add(uploadBlob(missingDigest, merkleTree, additionalInputs));
+      uploadFutures.add(uploadBlob(context, missingDigest, merkleTree, additionalInputs));
     }
 
     waitForBulkTransfer(uploadFutures, /* cancelRemainingOnInterrupt=*/ false);
   }
 
   private ListenableFuture<Void> uploadBlob(
-      Digest digest, MerkleTree merkleTree, Map<Digest, Message> additionalInputs) {
+      RemoteActionExecutionContext context,
+      Digest digest,
+      MerkleTree merkleTree,
+      Map<Digest, Message> additionalInputs) {
     Directory node = merkleTree.getDirectoryByDigest(digest);
     if (node != null) {
-      return cacheProtocol.uploadBlob(digest, node.toByteString());
+      return cacheProtocol.uploadBlob(context, digest, node.toByteString());
     }
 
     PathOrBytes file = merkleTree.getFileByDigest(digest);
     if (file != null) {
       if (file.getBytes() != null) {
-        return cacheProtocol.uploadBlob(digest, file.getBytes());
+        return cacheProtocol.uploadBlob(context, digest, file.getBytes());
       }
-      return cacheProtocol.uploadFile(digest, file.getPath());
+      return cacheProtocol.uploadFile(context, digest, file.getPath());
     }
 
     Message message = additionalInputs.get(digest);
     if (message != null) {
-      return cacheProtocol.uploadBlob(digest, message.toByteString());
+      return cacheProtocol.uploadBlob(context, digest, message.toByteString());
     }
 
     return Futures.immediateFailedFuture(
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 dfa368b..443d4a8 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
@@ -93,7 +93,6 @@
 import com.google.devtools.common.options.OptionsParsingResult;
 import io.grpc.CallCredentials;
 import io.grpc.ClientInterceptor;
-import io.grpc.Context;
 import io.grpc.ManagedChannel;
 import java.io.IOException;
 import java.util.HashSet;
@@ -501,14 +500,13 @@
             digestUtil,
             uploader.retain());
     uploader.release();
-    Context requestContext =
-        TracingMetadataUtils.contextWithMetadata(buildRequestId, invocationId, "bes-upload");
     buildEventArtifactUploaderFactoryDelegate.init(
         new ByteStreamBuildEventArtifactUploaderFactory(
             uploader,
             cacheClient,
             cacheChannel.authority(),
-            requestContext,
+            buildRequestId,
+            invocationId,
             remoteOptions.remoteInstanceName));
 
     if (enableRemoteExecution) {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteRepositoryRemoteExecutor.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteRepositoryRemoteExecutor.java
index 5d89161..f4a05dc 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteRepositoryRemoteExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteRepositoryRemoteExecutor.java
@@ -144,7 +144,8 @@
           additionalInputs.put(actionDigest, action);
           additionalInputs.put(commandHash, command);
 
-          remoteCache.ensureInputsPresent(merkleTree, additionalInputs);
+          remoteCache.ensureInputsPresent(
+              remoteActionExecutionContext, merkleTree, additionalInputs);
         }
 
         try (SilentCloseable c =
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
index fa99c63..3e9486c 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
@@ -337,7 +337,8 @@
                 additionalInputs.put(commandHash, command);
                 Duration networkTimeStart = networkTime.getDuration();
                 Stopwatch uploadTime = Stopwatch.createStarted();
-                remoteCache.ensureInputsPresent(merkleTree, additionalInputs);
+                remoteCache.ensureInputsPresent(
+                    remoteActionExecutionContext, merkleTree, additionalInputs);
                 // subtract network time consumed here to ensure wall clock during upload is not
                 // double
                 // counted, and metrics time computation does not exceed total time
diff --git a/src/main/java/com/google/devtools/build/lib/remote/common/RemoteCacheClient.java b/src/main/java/com/google/devtools/build/lib/remote/common/RemoteCacheClient.java
index caf497f..628d214 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/common/RemoteCacheClient.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/common/RemoteCacheClient.java
@@ -104,20 +104,23 @@
   /**
    * Uploads a {@code file} to the CAS.
    *
+   * @param context the context for the action.
    * @param digest The digest of the file.
    * @param file The file to upload.
    * @return A future representing pending completion of the upload.
    */
-  ListenableFuture<Void> uploadFile(Digest digest, Path file);
+  ListenableFuture<Void> uploadFile(RemoteActionExecutionContext context, Digest digest, Path file);
 
   /**
    * Uploads a BLOB to the CAS.
    *
+   * @param context the context for the action.
    * @param digest The digest of the blob.
    * @param data The BLOB to upload.
    * @return A future representing pending completion of the upload.
    */
-  ListenableFuture<Void> uploadBlob(Digest digest, ByteString data);
+  ListenableFuture<Void> uploadBlob(
+      RemoteActionExecutionContext context, Digest digest, ByteString data);
 
   /** Close resources associated with the remote cache. */
   void close();
diff --git a/src/main/java/com/google/devtools/build/lib/remote/disk/DiskAndRemoteCacheClient.java b/src/main/java/com/google/devtools/build/lib/remote/disk/DiskAndRemoteCacheClient.java
index 040a7d4..60c5016 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/disk/DiskAndRemoteCacheClient.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/disk/DiskAndRemoteCacheClient.java
@@ -65,11 +65,12 @@
   }
 
   @Override
-  public ListenableFuture<Void> uploadFile(Digest digest, Path file) {
+  public ListenableFuture<Void> uploadFile(
+      RemoteActionExecutionContext context, Digest digest, Path file) {
     try {
-      diskCache.uploadFile(digest, file).get();
+      diskCache.uploadFile(context, digest, file).get();
       if (!options.incompatibleRemoteResultsIgnoreDisk || options.remoteUploadLocalResults) {
-        remoteCache.uploadFile(digest, file).get();
+        remoteCache.uploadFile(context, digest, file).get();
       }
     } catch (ExecutionException e) {
       return Futures.immediateFailedFuture(e.getCause());
@@ -80,11 +81,12 @@
   }
 
   @Override
-  public ListenableFuture<Void> uploadBlob(Digest digest, ByteString data) {
+  public ListenableFuture<Void> uploadBlob(
+      RemoteActionExecutionContext context, Digest digest, ByteString data) {
     try {
-      diskCache.uploadBlob(digest, data).get();
+      diskCache.uploadBlob(context, digest, data).get();
       if (!options.incompatibleRemoteResultsIgnoreDisk || options.remoteUploadLocalResults) {
-        remoteCache.uploadBlob(digest, data).get();
+        remoteCache.uploadBlob(context, digest, data).get();
       }
     } catch (ExecutionException e) {
       return Futures.immediateFailedFuture(e.getCause());
diff --git a/src/main/java/com/google/devtools/build/lib/remote/disk/DiskCacheClient.java b/src/main/java/com/google/devtools/build/lib/remote/disk/DiskCacheClient.java
index 0278913..6778525 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/disk/DiskCacheClient.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/disk/DiskCacheClient.java
@@ -121,7 +121,8 @@
   public void close() {}
 
   @Override
-  public ListenableFuture<Void> uploadFile(Digest digest, Path file) {
+  public ListenableFuture<Void> uploadFile(
+      RemoteActionExecutionContext context, Digest digest, Path file) {
     try (InputStream in = file.getInputStream()) {
       saveFile(digest.getHash(), in, /* actionResult= */ false);
     } catch (IOException e) {
@@ -131,7 +132,8 @@
   }
 
   @Override
-  public ListenableFuture<Void> uploadBlob(Digest digest, ByteString data) {
+  public ListenableFuture<Void> uploadBlob(
+      RemoteActionExecutionContext context, Digest digest, ByteString data) {
     try (InputStream in = data.newInput()) {
       saveFile(digest.getHash(), in, /* actionResult= */ false);
     } catch (IOException e) {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/http/HttpCacheClient.java b/src/main/java/com/google/devtools/build/lib/remote/http/HttpCacheClient.java
index 7a67bb5..1d8a922 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/http/HttpCacheClient.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/http/HttpCacheClient.java
@@ -671,7 +671,8 @@
   }
 
   @Override
-  public ListenableFuture<Void> uploadFile(Digest digest, Path file) {
+  public ListenableFuture<Void> uploadFile(
+      RemoteActionExecutionContext context, Digest digest, Path file) {
     try {
       return uploadAsync(
           digest.getHash(), digest.getSizeBytes(), file.getInputStream(), /* casUpload= */ true);
@@ -682,7 +683,8 @@
   }
 
   @Override
-  public ListenableFuture<Void> uploadBlob(Digest digest, ByteString data) {
+  public ListenableFuture<Void> uploadBlob(
+      RemoteActionExecutionContext context, Digest digest, ByteString data) {
     return uploadAsync(
         digest.getHash(), digest.getSizeBytes(), data.newInput(), /* casUpload= */ true);
   }