@@ -430,6 +430,78 @@ service ContentAddressableStorage {
430430 rpc GetTree (GetTreeRequest ) returns (stream GetTreeResponse ) {
431431 option (google.api.http ) = { get : "/v2/{instance_name=**}/blobs/{root_digest.hash}/{root_digest.size_bytes}:getTree" };
432432 }
433+
434+ // Split a blob into chunks.
435+ //
436+ // This splitting API aims to reduce download traffic between a client and a
437+ // server, e.g., if a client needs to fetch a large blob that just has been
438+ // modified slightly since the last built. In this case, there is no need to
439+ // fetch the entire blob data, but just the binary differences between the two
440+ // blob versions, which are typically determined by content-defined chunking.
441+ //
442+ // Clients can use this API before downloading a blob to determine which parts
443+ // of the blob are already present locally and do not need to be downloaded
444+ // again. The server splits the blob into chunks according to a
445+ // content-defined chunking algorithm and returns a list of the chunk digests
446+ // in the order in which the chunks have to be concatenated to assemble the
447+ // requested blob.
448+ //
449+ // The client can expect certain guarantees from the server if a split request
450+ // is answered successfully:
451+ // 1. The blob chunks are stored in CAS.
452+ // 2. Concatenating the blob chunks in the order of the digest list returned
453+ // by the server results in the original blob.
454+ //
455+ // The usage of this API is optional but it allows clients to download only
456+ // the missing parts of a large blob instead of the entire blob data, which in
457+ // turn can considerably reduce download network traffic.
458+ //
459+ // Servers are free to implement this functionality, but they need to declare
460+ // whether they support it or not by setting the
461+ // [CacheCapabilities.blob_split_support][build.bazel.remote.execution.v2.CacheCapabilities.blob_split_support]
462+ // field accordingly.
463+ //
464+ // Errors:
465+ //
466+ // * `NOT_FOUND`: The requested blob is not present in the CAS.
467+ // * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the blob
468+ // chunks.
469+ rpc SplitBlob (SplitBlobRequest ) returns (SplitBlobResponse ) {
470+ option (google.api.http ) = { get : "/v2/{instance_name=**}/blobs/{blob_digest.hash}/{blob_digest.size_bytes}:splitBlob" };
471+ }
472+
473+ // Splice a blob from chunks.
474+ //
475+ // This is the complementary operation to the
476+ // [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob]
477+ // function to handle the splitted upload of large blobs to safe upload
478+ // traffic.
479+ //
480+ // If a client needs to upload a large blob and is able to split a blob into
481+ // chunks locally according to some content-defined chunking algorithm, it can
482+ // first determine which parts of the blob are already available in the remote
483+ // CAS and upload the missing chunks, and then use this API to instruct the
484+ // server to splice the original blob from the remotely available blob chunks.
485+ //
486+ // The usage of this API is optional but it allows clients to upload only the
487+ // missing parts of a large blob instead of the entire blob data, which in
488+ // turn can considerably reduce upload network traffic.
489+ //
490+ // Servers are free to implement this functionality, but they need to declare
491+ // whether they support it or not by setting the
492+ // [CacheCapabilities.blob_splice_support][build.bazel.remote.execution.v2.CacheCapabilities.blob_splice_support]
493+ // field accordingly.
494+ //
495+ // Errors:
496+ //
497+ // * `NOT_FOUND`: At least one of the blob chunks is not present in the CAS.
498+ // * `RESOURCE_EXHAUSTED`: There is insufficient disk quota to store the
499+ // spliced blob.
500+ // * `INVALID_ARGUMENT`: The digest of the spliced blob is different from the
501+ // provided expected digest.
502+ rpc SpliceBlob (SpliceBlobRequest ) returns (SpliceBlobResponse ) {
503+ option (google.api.http ) = { post : "/v2/{instance_name=**}/blobs:spliceBlob" body: "*" };
504+ }
433505}
434506
435507// The Capabilities service may be used by remote execution clients to query
@@ -1778,6 +1850,53 @@ message GetTreeResponse {
17781850 string next_page_token = 2 ;
17791851}
17801852
1853+ // A request message for
1854+ // [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob].
1855+ message SplitBlobRequest {
1856+ // The instance of the execution system to operate against. A server may
1857+ // support multiple instances of the execution system (with their own workers,
1858+ // storage, caches, etc.). The server MAY require use of this field to select
1859+ // between them in an implementation-defined fashion, otherwise it can be
1860+ // omitted.
1861+ string instance_name = 1 ;
1862+
1863+ // The digest of the blob to be splitted.
1864+ Digest blob_digest = 2 ;
1865+ }
1866+
1867+ // A response message for
1868+ // [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob].
1869+ message SplitBlobResponse {
1870+ // The ordered list of digests of the chunks into which the blob was splitted.
1871+ // The original blob is assembled by concatenating the chunk data according to
1872+ // the order of the digests given by this list.
1873+ repeated Digest chunk_digests = 1 ;
1874+ }
1875+
1876+ // A request message for
1877+ // [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob].
1878+ message SpliceBlobRequest {
1879+ // The instance of the execution system to operate against. A server may
1880+ // support multiple instances of the execution system (with their own workers,
1881+ // storage, caches, etc.). The server MAY require use of this field to select
1882+ // between them in an implementation-defined fashion, otherwise it can be
1883+ // omitted.
1884+ string instance_name = 1 ;
1885+
1886+ // Expected digest of the spliced blob.
1887+ Digest blob_digest = 2 ;
1888+
1889+ // The ordered list of digests of the chunks which need to be concatenated to
1890+ // assemble the original blob.
1891+ repeated Digest chunk_digests = 3 ;
1892+ }
1893+
1894+ // A response message for
1895+ // [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob].
1896+ message SpliceBlobResponse {
1897+ // Intentionally empty for now, but might need to be extended in future.
1898+ }
1899+
17811900// A request message for
17821901// [Capabilities.GetCapabilities][build.bazel.remote.execution.v2.Capabilities.GetCapabilities].
17831902message GetCapabilitiesRequest {
@@ -1997,6 +2116,20 @@ message CacheCapabilities {
19972116 // [BatchUpdateBlobs][build.bazel.remote.execution.v2.ContentAddressableStorage.BatchUpdateBlobs]
19982117 // requests.
19992118 repeated Compressor.Value supported_batch_update_compressors = 7 ;
2119+
2120+ // Whether blob splitting is supported for the particular server/instance. If
2121+ // yes, the server/instance implements the specified behavior for blob
2122+ // splitting and a meaningful result can be expected from the
2123+ // [ContentAddressableStorage.SplitBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SplitBlob]
2124+ // operation.
2125+ bool blob_split_support = 8 ;
2126+
2127+ // Whether blob splicing is supported for the particular server/instance. If
2128+ // yes, the server/instance implements the specified behavior for blob
2129+ // splicing and a meaningful result can be expected from the
2130+ // [ContentAddressableStorage.SpliceBlob][build.bazel.remote.execution.v2.ContentAddressableStorage.SpliceBlob]
2131+ // operation.
2132+ bool blob_splice_support = 9 ;
20002133}
20012134
20022135// Capabilities of the remote execution system.
0 commit comments