From b9f2dc28db658549ff0a862d126c3c826ef16ce8 Mon Sep 17 00:00:00 2001 From: BenWhitehead Date: Thu, 2 Oct 2025 18:47:41 -0400 Subject: [PATCH] fix: update PCU request building logic to properly clear crc32c and md5 If a PCU is initiated with a BlobInfo from the current generation of an object, the crc32c and md5 will be included in all compose requests. Update the code to properly clear when necessary, and to only apply a provided value to the final compose if crc32cMatch or md5Match were specified. --- ...CompositeUploadBlobWriteSessionConfig.java | 4 ++- ...lelCompositeUploadWritableByteChannel.java | 2 +- ...ompositeUploadWritableByteChannelTest.java | 36 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadBlobWriteSessionConfig.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadBlobWriteSessionConfig.java index 498947385e..d3f8764aed 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadBlobWriteSessionConfig.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadBlobWriteSessionConfig.java @@ -1025,7 +1025,9 @@ private ParallelCompositeUploadWriterFactory( @Override public WritableByteChannelSession writeSession( StorageInternal s, BlobInfo info, Opts opts) { - return new PCUSession(s, info, opts); + // if crc32cMatch or md5Match were specified, they will already be in opts + BlobInfo trimmed = info.toBuilder().clearCrc32c().clearMd5().build(); + return new PCUSession(s, trimmed, opts); } private final class PCUSession diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannel.java index 2bda77d036..464c6b2372 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannel.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannel.java @@ -425,7 +425,7 @@ private ApiFuture cleanupParts(BlobInfo finalInfo) { private BlobInfo definePart(BlobInfo ultimateObject, PartRange partRange, long offset) { BlobId id = ultimateObject.getBlobId(); - BlobInfo.Builder b = ultimateObject.toBuilder(); + BlobInfo.Builder b = ultimateObject.toBuilder().clearCrc32c().clearMd5(); String partName = partNamingStrategy.fmtName(id.getName(), partRange); b.setBlobId(BlobId.of(id.getBucket(), partName)); ImmutableMap.Builder builder = ImmutableMap.builder(); diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannelTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannelTest.java index 14e9ef1ec7..2990aae5dd 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannelTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannelTest.java @@ -39,6 +39,7 @@ import com.google.cloud.storage.UnifiedOpts.ObjectSourceOpt; import com.google.cloud.storage.UnifiedOpts.ObjectTargetOpt; import com.google.cloud.storage.UnifiedOpts.Opts; +import com.google.cloud.storage.it.ChecksummedTestContent; import com.google.cloud.storage.spi.v1.StorageRpc; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; @@ -810,6 +811,41 @@ public BlobInfo internalDirectUpload( pcu.close(); } + @Test + public void partDoesNotSpecifyAnyChecksum() throws IOException { + FakeStorageInternal storageInternal = + new FakeStorageInternal() { + @Override + public BlobInfo compose(ComposeRequest composeRequest) { + BlobInfo target = composeRequest.getTarget(); + if (target.getBlobId().getName().startsWith(partNamingStrategy.prefix)) { + assertThat(target.getCrc32c()).isNull(); + assertThat(target.getMd5()).isNull(); + } + return super.compose(composeRequest); + } + }; + ChecksummedTestContent content = ChecksummedTestContent.gen(bufferCapacity * 3 - 1); + ParallelCompositeUploadWritableByteChannel pcu = + new ParallelCompositeUploadWritableByteChannel( + bufferHandlePool, + MoreExecutors.directExecutor(), + partNamingStrategy, + PartCleanupStrategy.always(), + 2, + b -> b, + finalObject, + storageInternal, + info.toBuilder() + .setCrc32c(content.getCrc32cBase64()) + .setMd5(content.getMd5Base64()) + .build(), + opts); + pcu.write(content.asByteBuffer()); + + pcu.close(); + } + @NonNull private ParallelCompositeUploadWritableByteChannel defaultPcu(int maxElementsPerCompact) { return new ParallelCompositeUploadWritableByteChannel(