Skip to content

Replace CAS with CS and CLS - #347

Draft
meroton-benjamin wants to merge 2 commits into
buildbarn:mainfrom
meroton:cdc-support-step-2
Draft

Replace CAS with CS and CLS#347
meroton-benjamin wants to merge 2 commits into
buildbarn:mainfrom
meroton:cdc-support-step-2

Conversation

@meroton-benjamin

Copy link
Copy Markdown
Contributor

This commit builds on top of our split and splice blob support to make
it a mandatory first class feature in Buildbarn. With this commit the
Content Addressable Storage (CAS) is created from two Storage
configurations that work in tandem. A Chunk Storage (CS) which is
content addressed and contains chunks of blobs, and a Chunk List Storage
(CLS) which is addressed by a blob digest and contains a manifest
describing the chunks that make up the blob.

All api calls are automatically translated to use Chunk Lists created
with RepMaxCDC. Effectively this means that large blobs no longer exists
in the storage layer, individual chunks of the large blobs are in turn
deduplicated in such a manner that the chunks are stored only once.

The automatic translation makes certain that clients that are not cdc
aware can still continue to use the storage backend without performing
any changes. Clients which support RepMaxCDC also gets a significant
reduction in the amount of blobs to transfer as they only need to
transfer modified chunks rather than the entire blob.

@meroton-benjamin
meroton-benjamin force-pushed the cdc-support-step-2 branch 2 times, most recently from a6748b0 to c2f0bf7 Compare June 22, 2026 10:21
Comment thread cmd/bb_storage/main.go Outdated
Comment thread cmd/bb_storage/main.go Outdated
Comment thread cmd/bb_storage/main.go Outdated
Comment thread cmd/bb_storage/main.go Outdated

// Chunk list is marked for validation bypass, push it directy to
// downstream blob store.
if cdc.ChunkListValidationBypassed(ctx) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me the notion of whether a chunk list is known to be valid isn't really a properly of the calling context. It's more of a property of the chunk list that's passed in. Maybe better to either change the signature of BlobAccess.Put(), or add this to buffer.Buffer?

I take it that this logic is added to make sure that if a client performs a legacy Write() for a large object, that the frontend doesn't re-read the objects just to make sure that the ChunkList is valid, right? If so, how does this actually relate to buffer.Source? Maybe we should just treat ChunkLists created by bb-frontend itself as being buffer.BackendProvided(), and use that to skip validation?

Comment thread pkg/proto/configuration/blobstore/blobstore.proto Outdated
Comment thread pkg/proto/configuration/blobstore/blobstore.proto Outdated
Comment thread pkg/proto/configuration/bb_storage/bb_storage.proto Outdated
Comment thread pkg/proto/configuration/bb_storage/bb_storage.proto Outdated
Comment thread cmd/bb_storage/main.go Outdated
Comment on lines +66 to +71
if casConfiguration.ChunkStorage == nil {
return status.Error(codes.InvalidArgument, "The Chunk Storage is a mandatory part of the Content Addressable Storage.")
}
if casConfiguration.ChunkListStorage == nil {
return status.Error(codes.InvalidArgument, "The Chunk List Storage is a mandatory part of the Content Addressable Storage.")
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really a necessary requirement? I can imagine that for frontends that implement the full REv2 API it's necessary that both are provided. But for individual shards of our storage backends there is no requirement that each node provides both a CS and CLS.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concern still holds. :-)

@meroton-benjamin
meroton-benjamin force-pushed the cdc-support-step-2 branch 2 times, most recently from fe1daa0 to c2608ce Compare August 17, 2026 15:20
// an outer ChunkValidatingBlobAccess that has view of the entire
// Content Addressable Storage (CAS).
//
// This option is only supported for the CLS.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option is only supported for the CS?


// Optional: In memory cache for getting the capabilities for a
// specific instance name. Can be set to a relatively low value like
// '60s'.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used by the CS?

Comment thread cmd/bb_copy/main.go
grpcClientFactory := grpc.NewBaseClientFactory(grpc.BaseClientDialer, nil, nil, nil)

blobAccessCreator := blobstore_configuration.NewCASBlobAccessCreator(
blobAccessCreator := blobstore_configuration.NewCSBlobAccessCreator(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind that bb_copy can, for example, be used to make backups of actions. This means that for bb_copy it's not sufficient to only use a Chunk Store. It should also preserve/expand chunk lists, etc.

Comment thread cmd/bb_replicator/main.go
@@ -33,7 +33,7 @@ func main() {
return util.StatusWrap(err, "Failed to apply global configuration options")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we only deal with chunks and no longer do any streaming, do you think we still need bb_replicator? Maybe it's fine to just throw it out altogether, and let bb_frontend do the fixups directly?

Comment thread cmd/bb_storage/main.go Outdated
Comment on lines +66 to +71
if casConfiguration.ChunkStorage == nil {
return status.Error(codes.InvalidArgument, "The Chunk Storage is a mandatory part of the Content Addressable Storage.")
}
if casConfiguration.ChunkListStorage == nil {
return status.Error(codes.InvalidArgument, "The Chunk List Storage is a mandatory part of the Content Addressable Storage.")
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concern still holds. :-)

// NewCasChunkingBlobAccess creates a cas blob access configuration that
// constructs large case objects from the chunks described in the chunk
// list.
func NewCasChunkingBlobAccess(chunkStorage, chunkListStorage blobstore.BlobAccess, chunkListFetcher buffer.ChunkListFetcher, maximumMessageSizeBytes int) blobstore.BlobAccess {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type would also be removed, right?

Comment thread pkg/blobstore/cdc/cdc_parameters.go Outdated
if params == nil {
return Parameters{}, status.Error(codes.Unimplemented, "This server implementation is only compatible with RepMaxCDC")
}
if params.MinChunkSizeBytes < 64 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a safety belt, also require that MaxChunkSizeBytes isn't larger than some big constant? The same thing with the HorizonSizeBytes, which should also be limited. Maybe better to also require a MinChunSizeBytes of more than 64? Seems pretty evil if clients can be tricked into using a ridiculously small chunk size that would cause very high memory usage.

func (ba *chunkListValidatingBlobAccess) matchesStoredChunkList(ctx context.Context, d digest.Digest, userResponse *remoteexecution.SplitBlobResponse) bool {
existingMsg, err := ba.BlobAccess.Get(ctx, d).ToProto(&remoteexecution.SplitBlobResponse{}, ba.maximumMessageSizeBytes)
if err != nil {
return false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suppresses errors. Is that intentional?

},
}),
})
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that instead of doing this, we should:

  • Rename GetDefaultCapabilitiesProvider() to something like GetLeafCapabilitiesProvider(). Or GetBaseCapabilitiesProvider(). That way it's more explicit that it should only be used by 'leaf'/'base' data stores.
  • Extend that method to take a ChunkingParameters argument.
  • For the non-CS data stores, let it fail if chunking parameters are provided.
  • For the CS, do the inverse: require that chunking parameters are given.

// BlobAccessInfo for Content Addressable Storage (CAS) and BlobAccess
// objects for its constituent blobstores Chunk Storage (CS) and Chunk
// List Storage (CLS).
func NewCASBlobAccessFromConfiguration(terminationGroup program.Group, configuration *pb.ContentAddressableStorage, grpcClientFactory grpc.ClientFactory, maximumMessageSizeBytes int, zstdPool bb_zstd.Pool) (contentAddressableStorage BlobAccessInfo, chunkStorage, chunkListStorage blobstore.BlobAccess, err error) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to be removed, right?

This commit adds support for the SplitBlob and SpliceBlob methods from
the Remote Execution v2 (REv2) api. SplitBlob and SpliceBlob can be used
to facilitate uploads and downloads of large files but a naïve
implementation like this has some major drawbacks as well.

The blobs must exist in both their chunked and non chunked form,
which may significantly increase storage requirements for large blobs.
The protocol gives no guarantee that a large blob stored in the CAS
exists in its chunked form which forces you to perform a fairly heavy
Split call that loads the entire large blob in order to decomposition it
into its chunks.

This implementation mostly exists as a stepping stone for a different
implementation where Buildbarn internally manages all blobs as chunked
blobs.
This commit builds on top of our split and splice blob support to make
it a mandatory first class feature in Buildbarn. With this commit the
Content Addressable Storage (CAS) is created from two Storage
configurations that work in tandem. A Chunk Storage (CS) which is
content addressed and contains chunks of blobs, and a Chunk List Storage
(CLS) which is addressed by a blob digest and contains a manifest
describing the chunks that make up the blob.

All api calls are automatically translated to use Chunk Lists created
with RepMaxCDC. Effectively this means that large blobs no longer exists
in the storage layer, individual chunks of the large blobs are in turn
deduplicated in such a manner that the chunks are stored only once.

The automatic translation makes certain that clients that are not cdc
aware can still continue to use the storage backend without performing
any changes. Clients which support RepMaxCDC also gets a significant
reduction in the amount of blobs to transfer as they only need to
transfer modified chunks rather than the entire blob.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants