remote/http: distinquish between action cache and cas
The remote http caching client now prefixes the URL of an action cache
request with 'ac' and cas request with 'cas'. This is useful
as servers might want to distinquish between the action cache and the
cas.
Before this change:
(GET|PUT) /f141ae2d23d0f976599e678da1c51d82fedaf8b1
After this change:
(GET|PUT) /ac/f141ae2d23d0f976599e678da1c51d82fedaf8b1
(GET|PUT) /cas/f141ae2d23d0f976599e678da1c51d82fedaf8b1
This change will likely break any HTTP caching service that assumed the
old URL scheme.
TESTED: Manually using https://github.com/buchgr/bazel-remote
RELNOTES: The remote HTTP/1.1 caching client (--remote_rest_cache) now
distinquishes between action cache and CAS. The request URL for the
action cache is prefixed with 'ac' and the URL for the CAS
is prefixed with 'cas'.
PiperOrigin-RevId: 166831997
diff --git a/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java b/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java
index 1c4c462..645a693 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/SimpleBlobStoreActionCache.java
@@ -180,7 +180,7 @@
Digest stdout = uploadFileContents(outErr.getOutputPath());
result.setStdoutDigest(stdout);
}
- blobStore.put(
+ blobStore.putActionResult(
actionKey.getDigest().getHash(), new ByteArrayInputStream(result.build().toByteArray()));
}
@@ -269,7 +269,7 @@
return digest;
}
- public void downloadBlob(Digest digest, OutputStream out)
+ private void downloadBlob(Digest digest, OutputStream out)
throws IOException, InterruptedException {
if (digest.getSizeBytes() == 0) {
return;
@@ -300,18 +300,31 @@
public ActionResult getCachedActionResult(ActionKey actionKey)
throws IOException, InterruptedException {
try {
- byte[] data = downloadBlob(actionKey.getDigest());
+ byte[] data = downloadActionResult(actionKey.getDigest());
return ActionResult.parseFrom(data);
- } catch (InvalidProtocolBufferException e) {
- return null;
- } catch (CacheNotFoundException e) {
+ } catch (InvalidProtocolBufferException | CacheNotFoundException e) {
return null;
}
}
+ private byte[] downloadActionResult(Digest digest) throws IOException, InterruptedException {
+ if (digest.getSizeBytes() == 0) {
+ return new byte[0];
+ }
+ // This unconditionally downloads the whole blob into memory!
+ checkBlobSize(digest.getSizeBytes() / 1024, "Download Action Result");
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ boolean success = blobStore.getActionResult(digest.getHash(), out);
+ if (!success) {
+ throw new CacheNotFoundException(digest);
+ }
+ return out.toByteArray();
+ }
+
public void setCachedActionResult(ActionKey actionKey, ActionResult result)
throws IOException, InterruptedException {
- blobStore.put(actionKey.getDigest().getHash(), new ByteArrayInputStream(result.toByteArray()));
+ blobStore.putActionResult(actionKey.getDigest().getHash(),
+ new ByteArrayInputStream(result.toByteArray()));
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/ConcurrentMapBlobStore.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/ConcurrentMapBlobStore.java
index 179385c..82cd36b 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/ConcurrentMapBlobStore.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/ConcurrentMapBlobStore.java
@@ -43,11 +43,23 @@
}
@Override
+ public boolean getActionResult(String key, OutputStream out)
+ throws IOException, InterruptedException {
+ return get(key, out);
+ }
+
+ @Override
public void put(String key, InputStream in) throws IOException {
byte[] value = ByteStreams.toByteArray(in);
map.put(key, value);
}
@Override
+ public void putActionResult(String key, InputStream in)
+ throws IOException, InterruptedException {
+ put(key, in);
+ }
+
+ @Override
public void close() {}
}
\ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/OnDiskBlobStore.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/OnDiskBlobStore.java
index 8e78055..8d41023 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/OnDiskBlobStore.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/OnDiskBlobStore.java
@@ -46,6 +46,12 @@
}
@Override
+ public boolean getActionResult(String key, OutputStream out)
+ throws IOException, InterruptedException {
+ return get(key, out);
+ }
+
+ @Override
public void put(String key, InputStream in) throws IOException {
// Write a temporary file first, and then rename, to avoid data corruption in case of a crash.
Path temp = toPath(UUID.randomUUID().toString());
@@ -59,6 +65,11 @@
}
@Override
+ public void putActionResult(String key, InputStream in) throws IOException, InterruptedException {
+ put(key, in);
+ }
+
+ @Override
public void close() {}
private Path toPath(String key) {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/RestBlobStore.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/RestBlobStore.java
index 82e9b47..b7473d8 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/RestBlobStore.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/RestBlobStore.java
@@ -32,19 +32,22 @@
* Implementation of {@link SimpleBlobStore} with a REST service. The REST service needs to
* support the following HTTP methods.
*
- * <p>PUT /cache/1234 HTTP/1.1 PUT method is used to upload a blob with a base16 key. In this
- * example the key is 1234. Valid status codes are 200, 201, 202 and 204.
+ * <p>PUT /{actioncache,cas}/1234 HTTP/1.1 PUT method is used to upload a blob with a base16 key.
+ * In this example the key is 1234. Valid status codes are 200, 201, 202 and 204.
*
- * <p>GET /cache/1234 HTTP/1.1 GET method fetches a blob with the specified key. In this example
- * the key is 1234. A status code of 200 should be followed by the content of blob. Status code of
- * 404 or 204 means the key cannot be found.
+ * <p>GET /{actioncache,cas}/1234 HTTP/1.1 GET method fetches a blob with the specified key. In this
+ * example the key is 1234. A status code of 200 should be followed by the content of blob. Status
+ * code of 404 or 204 means the key cannot be found.
*
- * <p>HEAD /cache/1234 HTTP/1.1 HEAD method checks to see if the specified key exists in the blob
- * store. A status code of 200 indicates the key is found in the blob store. A status code of 404
- * indicates the key is not found in the blob store.
+ * <p>HEAD /{actioncache,cas}/1234 HTTP/1.1 HEAD method checks to see if the specified key exists in
+ * the blob store. A status code of 200 indicates the key is found in the blob store. A status code
+ * of 404 indicates the key is not found in the blob store.
*/
public final class RestBlobStore implements SimpleBlobStore {
+ private static final String ACTION_CACHE_PREFIX = "ac";
+ private static final String CAS_PREFIX = "cas";
+
private final String baseUrl;
private final PoolingHttpClientConnectionManager connMan;
private final HttpClientBuilder clientFactory;
@@ -85,8 +88,18 @@
@Override
public boolean get(String key, OutputStream out) throws IOException {
+ return get(CAS_PREFIX, key, out);
+ }
+
+ @Override
+ public boolean getActionResult(String key, OutputStream out)
+ throws IOException, InterruptedException {
+ return get(ACTION_CACHE_PREFIX, key, out);
+ }
+
+ private boolean get(String urlPrefix, String key, OutputStream out) throws IOException {
HttpClient client = clientFactory.build();
- HttpGet get = new HttpGet(baseUrl + "/" + key);
+ HttpGet get = new HttpGet(baseUrl + "/" + urlPrefix + "/" + key);
return client.execute(
get,
response -> {
@@ -105,8 +118,17 @@
@Override
public void put(String key, InputStream in) throws IOException {
+ put(CAS_PREFIX, key, in);
+ }
+
+ @Override
+ public void putActionResult(String key, InputStream in) throws IOException, InterruptedException {
+ put(ACTION_CACHE_PREFIX, key, in);
+ }
+
+ private void put(String urlPrefix, String key, InputStream in) throws IOException {
HttpClient client = clientFactory.build();
- HttpPut put = new HttpPut(baseUrl + "/" + key);
+ HttpPut put = new HttpPut(baseUrl + "/" + urlPrefix + "/" + key);
// For now, upload a byte array instead of a stream, due to Hazelcast crashing on the stream.
// See https://github.com/hazelcast/hazelcast/issues/10878.
put.setEntity(new ByteArrayEntity(ByteStreams.toByteArray(in)));
diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/SimpleBlobStore.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/SimpleBlobStore.java
index 5f79c45..3847996 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/SimpleBlobStore.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/SimpleBlobStore.java
@@ -19,25 +19,42 @@
import java.io.OutputStream;
/**
- * A simple interface for storing blobs (in the form of byte arrays) each one indexed by a
- * hexadecimal string. Implementation must be thread-safe.
+ * An interface for storing BLOBs each one indexed by a string (hash in hexadecimal).
+ *
+ * <p>Implementations must be thread-safe.
*/
public interface SimpleBlobStore {
- /** Returns true if the provided {@code key} is stored in the blob store. */
+ /**
+ * Returns {@code key} if the provided {@code key} is stored in the CAS.
+ */
boolean containsKey(String key) throws IOException, InterruptedException;
/**
- * Returns the blob (in the form of a byte array) indexed by {@code key}. Returns null if the
- * {@code key} cannot be found.
+ * Fetches the BLOB associated with the {@code key} from the CAS and writes it to {@code out}.
+ *
+ * @return {@code true} if the {@code key} was found. {@code false} otherwise.
*/
boolean get(String key, OutputStream out) throws IOException, InterruptedException;
/**
- * Uploads a blob (as {@code value}) indexed by {@code key} to the blob store. Existing blob
- * indexed by the same {@code key} will be overwritten.
+ * Fetches the BLOB associated with the {@code key} from the Action Cache and writes it to
+ * {@code out}.
+ *
+ * @return {@code true} if the {@code key} was found. {@code false} otherwise.
+ */
+ boolean getActionResult(String actionKey, OutputStream out) throws IOException,
+ InterruptedException;
+
+ /**
+ * Uploads a BLOB (as {@code in}) indexed by {@code key} to the CAS.
*/
void put(String key, InputStream in) throws IOException, InterruptedException;
+ /**
+ * Uploads a BLOB (as {@code in}) indexed by {@code key} to the Action Cache.
+ */
+ void putActionResult(String actionKey, InputStream in) throws IOException, InterruptedException;
+
/** Close resources associated with the blob store. */
void close();
}