Skip to content

Commit fe1daa0

Browse files
Replace CAS with CS and CLS
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.
1 parent 190edf7 commit fe1daa0

108 files changed

Lines changed: 4083 additions & 3026 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/bb_copy/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func main() {
4242

4343
grpcClientFactory := grpc.NewBaseClientFactory(grpc.BaseClientDialer, nil, nil, nil)
4444

45-
blobAccessCreator := blobstore_configuration.NewCASBlobAccessCreator(
45+
blobAccessCreator := blobstore_configuration.NewCSBlobAccessCreator(
4646
grpcClientFactory,
4747
int(configuration.MaximumMessageSizeBytes),
4848
bb_zstd.NewPoolFromConfiguration(nil),
@@ -68,7 +68,7 @@ func main() {
6868
configuration.Replicator,
6969
source.BlobAccess,
7070
sink,
71-
blobstore_configuration.NewCASBlobReplicatorCreator(grpcClientFactory),
71+
blobstore_configuration.NewCSBlobReplicatorCreator(grpcClientFactory),
7272
)
7373
if err != nil {
7474
return util.StatusWrap(err, "Failed to create replicator")

cmd/bb_replicator/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func main() {
3333
return util.StatusWrap(err, "Failed to apply global configuration options")
3434
}
3535

36-
blobAccessCreator := blobstore_configuration.NewCASBlobAccessCreator(
36+
blobAccessCreator := blobstore_configuration.NewCSBlobAccessCreator(
3737
grpcClientFactory,
3838
int(configuration.MaximumMessageSizeBytes),
3939
bb_zstd.NewPoolFromConfiguration(nil),
@@ -59,7 +59,7 @@ func main() {
5959
configuration.Replicator,
6060
source.BlobAccess,
6161
sink,
62-
blobstore_configuration.NewCASBlobReplicatorCreator(grpcClientFactory),
62+
blobstore_configuration.NewCSBlobReplicatorCreator(grpcClientFactory),
6363
)
6464
if err != nil {
6565
return util.StatusWrap(err, "Failed to create replicator")

cmd/bb_storage/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@ go_library(
1010
"//pkg/auth",
1111
"//pkg/auth/configuration",
1212
"//pkg/blobstore",
13+
"//pkg/blobstore/cdc",
1314
"//pkg/blobstore/configuration",
1415
"//pkg/blobstore/grpcservers",
1516
"//pkg/builder",
1617
"//pkg/capabilities",
18+
"//pkg/clock",
19+
"//pkg/digest",
1720
"//pkg/global",
1821
"//pkg/grpc",
1922
"//pkg/program",
2023
"//pkg/proto/configuration/bb_storage",
2124
"//pkg/proto/fsac",
2225
"//pkg/proto/icas",
2326
"//pkg/proto/iscc",
27+
"//pkg/ttlcache",
2428
"//pkg/util",
2529
"//pkg/zstd",
2630
"@bazel_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto",

cmd/bb_storage/main.go

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/buildbarn/bb-storage/pkg/auth"
1010
auth_configuration "github.com/buildbarn/bb-storage/pkg/auth/configuration"
1111
"github.com/buildbarn/bb-storage/pkg/blobstore"
12+
"github.com/buildbarn/bb-storage/pkg/blobstore/cdc"
1213
blobstore_configuration "github.com/buildbarn/bb-storage/pkg/blobstore/configuration"
1314
"github.com/buildbarn/bb-storage/pkg/blobstore/grpcservers"
1415
"github.com/buildbarn/bb-storage/pkg/builder"
@@ -54,55 +55,57 @@ func main() {
5455
var cacheCapabilitiesAuthorizers []auth.Authorizer
5556

5657
// Content Addressable Storage (CAS).
57-
var contentAddressableStorageInfo *blobstore_configuration.BlobAccessInfo
58-
var contentAddressableStorage blobstore.BlobAccess
59-
if configuration.ContentAddressableStorage != nil {
60-
info, authorizedBackend, allAuthorizers, err := newScannableBlobAccess(
58+
var contentAddressableStorage cdc.ContentAddressableStorage
59+
var authorizedContentAddressableStorage cdc.ContentAddressableStorage
60+
if configuration.ContentAddressableStorageServer != nil {
61+
cas, chunkStorage, chunkListStorage, _, cdcParametersFetcher, err := blobstore_configuration.NewCASFromConfiguration(
6162
dependenciesGroup,
62-
configuration.ContentAddressableStorage,
63-
blobstore_configuration.NewCASBlobAccessCreator(
64-
grpcClientFactory,
65-
int(configuration.MaximumMessageSizeBytes),
66-
zstdPool,
67-
),
63+
configuration.ContentAddressableStorageServer.ContentAddressableStorage,
6864
grpcClientFactory,
65+
int(configuration.MaximumMessageSizeBytes),
66+
zstdPool,
6967
)
7068
if err != nil {
7169
return util.StatusWrap(err, "Failed to create Content Addressable Storage")
7270
}
71+
contentAddressableStorage = cas
72+
73+
// Create authorizers.
74+
getAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.ContentAddressableStorageServer.GetAuthorizer, dependenciesGroup, grpcClientFactory)
75+
if err != nil {
76+
return util.StatusWrap(err, "Failed to create Get() authorizer for Content Addressable Storage")
77+
}
78+
putAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.ContentAddressableStorageServer.PutAuthorizer, dependenciesGroup, grpcClientFactory)
79+
if err != nil {
80+
return util.StatusWrap(err, "Failed to create Put() authorizer for Content Addressable Storage")
81+
}
82+
findMissingAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.ContentAddressableStorageServer.FindMissingAuthorizer, dependenciesGroup, grpcClientFactory)
83+
if err != nil {
84+
return util.StatusWrap(err, "Failed to create FindMissing() authorizer for Content Addressable Storage")
85+
}
86+
87+
// Create authorized versions of the backends.
88+
authorizedChunkStorage := blobstore.NewAuthorizingBlobAccess(chunkStorage, getAuthorizer, putAuthorizer, findMissingAuthorizer)
89+
authorizedChunkListStorage := blobstore.NewAuthorizingBlobAccess(chunkListStorage, getAuthorizer, putAuthorizer, findMissingAuthorizer)
90+
authorizedChunkListFetcher := blobstore.NewBlobAccessChunkListFetcher(authorizedChunkListStorage, int(configuration.MaximumMessageSizeBytes))
91+
authorizedContentAddressableStorage = cdc.NewContentAddressableStorage(
92+
authorizedChunkStorage,
93+
authorizedChunkListStorage,
94+
authorizedChunkListFetcher,
95+
cdcParametersFetcher,
96+
contentAddressableStorage.GetDigestKeyFormat(),
97+
)
98+
// Create the Chunk Storage (CS).
7399
cacheCapabilitiesProviders = append(
74100
cacheCapabilitiesProviders,
75-
info.BlobAccess,
101+
chunkStorage,
76102
capabilities.NewStaticProvider(&remoteexecution.ServerCapabilities{
77103
CacheCapabilities: &remoteexecution.CacheCapabilities{
78104
SupportedCompressors: configuration.SupportedCompressors,
79105
},
80106
}),
81107
)
82-
cacheCapabilitiesAuthorizers = append(cacheCapabilitiesAuthorizers, allAuthorizers...)
83-
contentAddressableStorageInfo = &info
84-
contentAddressableStorage = authorizedBackend
85-
}
86-
87-
// Chunk List Storage (CLS).
88-
var chunkListStorage blobstore.BlobAccess
89-
if configuration.ChunkListStorage != nil {
90-
info, authorizedBackend, allAuthorizers, err := newScannableBlobAccess(
91-
dependenciesGroup,
92-
configuration.ChunkListStorage,
93-
blobstore_configuration.NewCLSBlobAccessCreator(
94-
contentAddressableStorageInfo,
95-
grpcClientFactory,
96-
int(configuration.MaximumMessageSizeBytes),
97-
),
98-
grpcClientFactory,
99-
)
100-
if err != nil {
101-
return util.StatusWrap(err, "Failed to create Chunk List Storage")
102-
}
103-
cacheCapabilitiesProviders = append(cacheCapabilitiesProviders, info.BlobAccess)
104-
cacheCapabilitiesAuthorizers = append(cacheCapabilitiesAuthorizers, allAuthorizers...)
105-
chunkListStorage = authorizedBackend
108+
cacheCapabilitiesAuthorizers = append(cacheCapabilitiesAuthorizers, getAuthorizer, putAuthorizer, findMissingAuthorizer)
106109
}
107110

108111
// Action Cache (AC).
@@ -112,7 +115,7 @@ func main() {
112115
dependenciesGroup,
113116
configuration.ActionCache,
114117
blobstore_configuration.NewACBlobAccessCreator(
115-
contentAddressableStorageInfo,
118+
contentAddressableStorage,
116119
grpcClientFactory,
117120
int(configuration.MaximumMessageSizeBytes),
118121
),
@@ -213,10 +216,9 @@ func main() {
213216
if err := bb_grpc.NewServersFromConfigurationAndServe(
214217
configuration.GrpcServers,
215218
func(s grpc.ServiceRegistrar) {
216-
if contentAddressableStorage != nil {
219+
if authorizedContentAddressableStorage != nil {
217220
contentAddressableStorageServer := grpcservers.NewContentAddressableStorageServer(
218-
contentAddressableStorage,
219-
chunkListStorage,
221+
authorizedContentAddressableStorage,
220222
configuration.MaximumMessageSizeBytes,
221223
)
222224
remoteexecution.RegisterContentAddressableStorageServer(
@@ -226,8 +228,7 @@ func main() {
226228
bytestream.RegisterByteStreamServer(
227229
s,
228230
grpcservers.NewByteStreamServer(
229-
contentAddressableStorage,
230-
1<<16,
231+
authorizedContentAddressableStorage,
231232
zstdPool,
232233
),
233234
)

internal/mock/BUILD.bazel

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,19 @@ gomock(
152152
package = "mock",
153153
)
154154

155+
gomock(
156+
name = "cdc",
157+
out = "cdc.go",
158+
interfaces = [
159+
"ContentAddressableStorage",
160+
"ParametersFetcher",
161+
],
162+
library = "//pkg/blobstore/cdc",
163+
mockgen_model_library = "@org_uber_go_mock//mockgen/model",
164+
mockgen_tool = "@org_uber_go_mock//mockgen",
165+
package = "mock",
166+
)
167+
155168
gomock(
156169
name = "clock",
157170
out = "clock.go",
@@ -367,6 +380,7 @@ go_library(
367380
"buffer.go",
368381
"builder.go",
369382
"capabilities.go",
383+
"cdc.go",
370384
"clock.go",
371385
"cloud_aws.go",
372386
"cloud_gcp.go",
@@ -391,6 +405,8 @@ go_library(
391405
"//pkg/auth",
392406
"//pkg/blobstore",
393407
"//pkg/blobstore/buffer",
408+
"//pkg/blobstore/cdc",
409+
"//pkg/blobstore/chunklist",
394410
"//pkg/blobstore/local",
395411
"//pkg/blobstore/sharding",
396412
"//pkg/blobstore/slicing",

pkg/blobstore/BUILD.bazel

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ go_library(
99
"authorizing_blob_access.go",
1010
"blob_access.go",
1111
"cas_read_buffer_factory.go",
12+
"chunk_list_fetcher.go",
1213
"cls_read_buffer_factory.go",
1314
"deadline_enforcing_blob_access.go",
1415
"demultiplexing_blob_access.go",
@@ -22,7 +23,6 @@ go_library(
2223
"metrics_blob_access.go",
2324
"read_buffer_factory.go",
2425
"read_canarying_blob_access.go",
25-
"reference_expanding_blob_access.go",
2626
"validation_caching_read_buffer_factory.go",
2727
"visit_topologically_sorted_tree.go",
2828
"zip_reading_blob_access.go",
@@ -33,21 +33,18 @@ go_library(
3333
deps = [
3434
"//pkg/auth",
3535
"//pkg/blobstore/buffer",
36+
"//pkg/blobstore/chunklist",
3637
"//pkg/blobstore/slicing",
3738
"//pkg/capabilities",
3839
"//pkg/clock",
39-
"//pkg/cloud/aws",
40-
"//pkg/cloud/gcp",
4140
"//pkg/digest",
4241
"//pkg/eviction",
42+
"//pkg/proto/blobstore/chunklist",
4343
"//pkg/proto/fsac",
4444
"//pkg/proto/icas",
4545
"//pkg/proto/iscc",
4646
"//pkg/util",
47-
"//pkg/zstd",
4847
"@bazel_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto",
49-
"@com_github_aws_aws_sdk_go_v2//aws",
50-
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
5148
"@com_github_prometheus_client_golang//prometheus",
5249
"@org_golang_google_grpc//codes",
5350
"@org_golang_google_grpc//status",
@@ -68,7 +65,6 @@ go_test(
6865
"existence_caching_blob_access_test.go",
6966
"hierarchical_instance_names_blob_access_test.go",
7067
"read_canarying_blob_access_test.go",
71-
"reference_expanding_blob_access_test.go",
7268
"validation_caching_read_buffer_factory_test.go",
7369
"visit_topologically_sorted_tree_test.go",
7470
"zip_reading_blob_access_test.go",
@@ -80,16 +76,10 @@ go_test(
8076
"//pkg/blobstore/buffer",
8177
"//pkg/digest",
8278
"//pkg/eviction",
83-
"//pkg/proto/icas",
8479
"//pkg/testutil",
8580
"//pkg/util",
86-
"//pkg/zstd",
8781
"@bazel_remote_apis//build/bazel/remote/execution/v2:remote_execution_go_proto",
8882
"@bazel_remote_apis//build/bazel/semver:semver_go_proto",
89-
"@com_github_aws_aws_sdk_go_v2//aws",
90-
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
91-
"@com_github_aws_aws_sdk_go_v2_service_s3//types",
92-
"@com_github_klauspost_compress//zstd",
9383
"@com_github_stretchr_testify//require",
9484
"@org_golang_google_grpc//codes",
9585
"@org_golang_google_grpc//status",

pkg/blobstore/authorizing_blob_access.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55

66
"github.com/buildbarn/bb-storage/pkg/auth"
77
"github.com/buildbarn/bb-storage/pkg/blobstore/buffer"
8-
"github.com/buildbarn/bb-storage/pkg/blobstore/slicing"
98
"github.com/buildbarn/bb-storage/pkg/digest"
109
"github.com/buildbarn/bb-storage/pkg/util"
1110
)
@@ -38,13 +37,6 @@ func (ba *authorizingBlobAccess) Get(ctx context.Context, d digest.Digest) buffe
3837
return ba.BlobAccess.Get(ctx, d)
3938
}
4039

41-
func (ba *authorizingBlobAccess) GetFromComposite(ctx context.Context, parentDigest, childDigest digest.Digest, slicer slicing.BlobSlicer) buffer.Buffer {
42-
if err := auth.AuthorizeSingleInstanceName(ctx, ba.getAuthorizer, parentDigest.GetInstanceName()); err != nil {
43-
return buffer.NewBufferFromError(util.StatusWrap(err, "Authorization"))
44-
}
45-
return ba.BlobAccess.GetFromComposite(ctx, parentDigest, childDigest, slicer)
46-
}
47-
4840
func (ba *authorizingBlobAccess) Put(ctx context.Context, d digest.Digest, b buffer.Buffer) error {
4941
if err := auth.AuthorizeSingleInstanceName(ctx, ba.putAuthorizer, d.GetInstanceName()); err != nil {
5042
return util.StatusWrap(err, "Authorization")

pkg/blobstore/authorizing_blob_access_test.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,6 @@ func TestAuthorizingBlobAccess(t *testing.T) {
5454
testutil.RequireEqualStatus(t, status.Error(codes.PermissionDenied, "Authorization: You shall not pass"), err)
5555
})
5656

57-
t.Run("GetFromComposite-Allowed", func(t *testing.T) {
58-
getAuthorizer.EXPECT().Authorize(ctx, beepSlice).Return([]error{nil})
59-
blobSlicer := mock.NewMockBlobSlicer(ctrl)
60-
baseBlobAccess.EXPECT().GetFromComposite(ctx, d, d2, blobSlicer).Return(wantBuf)
61-
62-
gotBuf, err := ba.GetFromComposite(ctx, d, d2, blobSlicer).ToByteSlice(30)
63-
require.NoError(t, err)
64-
require.Equal(t, wantBytes, gotBuf)
65-
})
66-
67-
t.Run("GetFromComposite-Denied", func(t *testing.T) {
68-
blobSlicer := mock.NewMockBlobSlicer(ctrl)
69-
getAuthorizer.EXPECT().Authorize(ctx, beepSlice).Return([]error{status.Error(codes.PermissionDenied, "You shall not pass")})
70-
71-
_, err := ba.GetFromComposite(ctx, d, d2, blobSlicer).ToByteSlice(30)
72-
testutil.RequireEqualStatus(t, status.Error(codes.PermissionDenied, "Authorization: You shall not pass"), err)
73-
})
74-
7557
t.Run("Put-Allowed", func(t *testing.T) {
7658
putAuthorizer.EXPECT().Authorize(ctx, beepSlice).Return([]error{nil})
7759
baseBlobAccess.EXPECT().Put(ctx, d, wantBuf).Return(nil)

pkg/blobstore/blob_access.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55

66
"github.com/buildbarn/bb-storage/pkg/blobstore/buffer"
7-
"github.com/buildbarn/bb-storage/pkg/blobstore/slicing"
87
"github.com/buildbarn/bb-storage/pkg/capabilities"
98
"github.com/buildbarn/bb-storage/pkg/digest"
109
)
@@ -16,7 +15,6 @@ type BlobAccess interface {
1615
capabilities.Provider
1716

1817
Get(ctx context.Context, digest digest.Digest) buffer.Buffer
19-
GetFromComposite(ctx context.Context, parentDigest, childDigest digest.Digest, slicer slicing.BlobSlicer) buffer.Buffer
2018
Put(ctx context.Context, digest digest.Digest, b buffer.Buffer) error
2119
FindMissing(ctx context.Context, digests digest.Set) (digest.Set, error)
2220
}

0 commit comments

Comments
 (0)