Skip to content

Commit

Permalink
Support new-style digest functions
Browse files Browse the repository at this point in the history
Support new-style digest functions.

This PR adds support for new-style digest functions to the remote execution
library code. The remote-apis spec says:

```
// * `digest_function` is a lowercase string form of a `DigestFunction.Value`
//   enum, indicating which digest function was used to compute `hash`. If the
//   digest function used is one of MD5, MURMUR3, SHA1, SHA256, SHA384, SHA512,
//   or VSO, this component MUST be omitted. In that case the server SHOULD
//   infer the digest function using the length of the `hash` and the digest
//   functions announced in the server's capabilities.
```

This is a partial commit for #18658.

Closes #18731.

PiperOrigin-RevId: 543691155
Change-Id: If8c386d923db1b24dff6054c8ab3f783409b7f13
  • Loading branch information
tylerwilliams authored and copybara-github committed Jun 27, 2023
1 parent 5fa4718 commit 88412ce
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static java.util.concurrent.TimeUnit.SECONDS;

import build.bazel.remote.execution.v2.Digest;
import build.bazel.remote.execution.v2.DigestFunction;
import com.google.bytestream.ByteStreamGrpc;
import com.google.bytestream.ByteStreamGrpc.ByteStreamFutureStub;
import com.google.bytestream.ByteStreamGrpc.ByteStreamStub;
Expand Down Expand Up @@ -69,6 +70,7 @@ final class ByteStreamUploader {
private final CallCredentialsProvider callCredentialsProvider;
private final long callTimeoutSecs;
private final RemoteRetrier retrier;
private final DigestFunction.Value digestFunction;

@Nullable private final Semaphore openedFilePermits;

Expand All @@ -89,14 +91,16 @@ final class ByteStreamUploader {
CallCredentialsProvider callCredentialsProvider,
long callTimeoutSecs,
RemoteRetrier retrier,
int maximumOpenFiles) {
int maximumOpenFiles,
DigestFunction.Value digestFunction) {
checkArgument(callTimeoutSecs > 0, "callTimeoutSecs must be gt 0.");
this.instanceName = instanceName;
this.channel = channel;
this.callCredentialsProvider = callCredentialsProvider;
this.callTimeoutSecs = callTimeoutSecs;
this.retrier = retrier;
this.openedFilePermits = maximumOpenFiles != -1 ? new Semaphore(maximumOpenFiles) : null;
this.digestFunction = digestFunction;
}

@VisibleForTesting
Expand Down Expand Up @@ -175,11 +179,34 @@ public ListenableFuture<Void> uploadBlobAsync(
MoreExecutors.directExecutor());
}

private static String buildUploadResourceName(
private boolean isOldStyleDigestFunction() {
// Old-style digest functions (SHA256, etc) are distinguishable by the length
// of their hash alone and do not require extra specification, but newer
// digest functions (which may have the same length hashes as the older
// functions!) must be explicitly specified in the upload resource name.
return digestFunction.getNumber() <= 7;
}

private String buildUploadResourceName(
String instanceName, UUID uuid, Digest digest, boolean compressed) {
String template =
compressed ? "uploads/%s/compressed-blobs/zstd/%s/%d" : "uploads/%s/blobs/%s/%d";
String resourceName = format(template, uuid, digest.getHash(), digest.getSizeBytes());

String resourceName;

if (isOldStyleDigestFunction()) {
String template =
compressed ? "uploads/%s/compressed-blobs/zstd/%s/%d" : "uploads/%s/blobs/%s/%d";
resourceName = format(template, uuid, digest.getHash(), digest.getSizeBytes());
} else {
String template =
compressed ? "uploads/%s/compressed-blobs/zstd/%s/%s/%d" : "uploads/%s/blobs/%s/%s/%d";
resourceName =
format(
template,
uuid,
Ascii.toLowerCase(digestFunction.getValueDescriptor().getName()),
digest.getHash(),
digest.getSizeBytes());
}
if (!Strings.isNullOrEmpty(instanceName)) {
resourceName = instanceName + "/" + resourceName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import build.bazel.remote.execution.v2.ContentAddressableStorageGrpc;
import build.bazel.remote.execution.v2.ContentAddressableStorageGrpc.ContentAddressableStorageFutureStub;
import build.bazel.remote.execution.v2.Digest;
import build.bazel.remote.execution.v2.DigestFunction;
import build.bazel.remote.execution.v2.FindMissingBlobsRequest;
import build.bazel.remote.execution.v2.FindMissingBlobsResponse;
import build.bazel.remote.execution.v2.GetActionResultRequest;
Expand Down Expand Up @@ -107,7 +108,8 @@ public GrpcCacheClient(
callCredentialsProvider,
options.remoteTimeout.getSeconds(),
retrier,
options.maximumOpenFiles);
options.maximumOpenFiles,
digestUtil.getDigestFunction());
maxMissingBlobsDigestsPerMessage = computeMaxMissingBlobsDigestsPerMessage();
Preconditions.checkState(
maxMissingBlobsDigestsPerMessage > 0, "Error: gRPC message size too small.");
Expand Down Expand Up @@ -352,12 +354,24 @@ private ListenableFuture<Void> downloadBlob(
MoreExecutors.directExecutor());
}

public static String getResourceName(String instanceName, Digest digest, boolean compressed) {
private static boolean isOldStyleDigestFunction(DigestFunction.Value digestFunction) {
// Old-style digest functions (SHA256, etc) are distinguishable by the length
// of their hash alone and do not require extra specification, but newer
// digest functions (which may have the same length hashes as the older
// functions!) must be explicitly specified in the upload resource name.
return digestFunction.getNumber() <= 7;
}

public static String getResourceName(
String instanceName, Digest digest, boolean compressed, DigestFunction.Value digestFunction) {
String resourceName = "";
if (!instanceName.isEmpty()) {
resourceName += instanceName + "/";
}
resourceName += compressed ? "compressed-blobs/zstd/" : "blobs/";
if (!isOldStyleDigestFunction(digestFunction)) {
resourceName += Ascii.toLowerCase(digestFunction.getValueDescriptor().getName()) + "/";
}
return resourceName + DigestUtil.toString(digest);
}

Expand All @@ -369,7 +383,11 @@ private ListenableFuture<Long> requestRead(
@Nullable Supplier<Digest> digestSupplier,
Channel channel) {
String resourceName =
getResourceName(options.remoteInstanceName, digest, options.cacheCompression);
getResourceName(
options.remoteInstanceName,
digest,
options.cacheCompression,
digestUtil.getDigestFunction());
SettableFuture<Long> future = SettableFuture.create();
OutputStream out;
try {
Expand Down
Loading

0 comments on commit 88412ce

Please sign in to comment.