From 3d93e580d7d8af7c17d10f3c94983060d2bb4aaf Mon Sep 17 00:00:00 2001 From: rpodar Date: Fri, 5 Dec 2025 13:19:45 +0530 Subject: [PATCH 1/2] Don't Checkin - API Implementation for ResumeAppendableObjectUpload --- google/cloud/storage/async/client.cc | 15 +++++++ google/cloud/storage/async/client.h | 13 ++++++ google/cloud/storage/async/client_test.cc | 49 +++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/google/cloud/storage/async/client.cc b/google/cloud/storage/async/client.cc index 509b6294a7a96..088e893154659 100644 --- a/google/cloud/storage/async/client.cc +++ b/google/cloud/storage/async/client.cc @@ -165,6 +165,21 @@ AsyncClient::ResumeAppendableObjectUpload(BucketName const& bucket_name, }); } +future>> +AsyncClient::ResumeAppendableObjectUpload( + google::storage::v2::BidiWriteObjectRequest request, Options opts) { + return connection_ + ->ResumeAppendableObjectUpload( + {std::move(request), + internal::MergeOptions(std::move(opts), connection_->options())}) + .then([](auto f) -> StatusOr> { + auto w = f.get(); + if (!w) return std::move(w).status(); + auto t = storage_internal::MakeAsyncToken(w->get()); + return std::make_pair(AsyncWriter(*std::move(w)), std::move(t)); + }); +} + future>> AsyncClient::StartBufferedUpload(BucketName const& bucket_name, std::string object_name, Options opts) { diff --git a/google/cloud/storage/async/client.h b/google/cloud/storage/async/client.h index f1514a810f54d..7c8c0eb5c7bf2 100644 --- a/google/cloud/storage/async/client.h +++ b/google/cloud/storage/async/client.h @@ -440,6 +440,19 @@ class AsyncClient { std::string object_name, std::int64_t generation, Options opts = {}); + /** + * Resume a resumable upload session for appendable objects and automatic + * recovery from transient failures. + * + * @param request the request contents, it must include the bucket name, + * object name, and generation. Many other fields are optional. + * @param opts options controlling the behaviour of this RPC, for example the + * application may change the retry policy. + */ + future>> + ResumeAppendableObjectUpload( + google::storage::v2::BidiWriteObjectRequest request, Options opts = {}); + /* [start-buffered-upload-common] This function always uses [resumable uploads][resumable-link]. The objects diff --git a/google/cloud/storage/async/client_test.cc b/google/cloud/storage/async/client_test.cc index 7feddd2627a64..ed089792262e1 100644 --- a/google/cloud/storage/async/client_test.cc +++ b/google/cloud/storage/async/client_test.cc @@ -1398,6 +1398,55 @@ TEST(AsyncClient, ResumeRewrite2) { } } // namespace + +TEST(AsyncClient, ResumeAppendableObjectUpload2) { + auto constexpr kExpectedRequest = R"pb( + append_object_spec { + bucket: "projects/_/buckets/test-bucket", + object: "test-object", + generation: 42 + } + flush: true + )pb"; + auto mock = std::make_shared(); + EXPECT_CALL(*mock, options) + .WillRepeatedly( + Return(Options{}.set>("O0").set>("O1"))); + + EXPECT_CALL(*mock, ResumeAppendableObjectUpload) + .WillOnce([&](AsyncConnection::AppendableUploadParams const& p) { + EXPECT_THAT(p.options.get>(), "O0"); + EXPECT_THAT(p.options.get>(), "O1-function"); + EXPECT_THAT(p.options.get>(), "O2-function"); + auto expected = google::storage::v2::BidiWriteObjectRequest{}; + EXPECT_TRUE(TextFormat::ParseFromString(kExpectedRequest, &expected)); + EXPECT_THAT(p.request, IsProtoEqual(expected)); + auto writer = std::make_unique(); + EXPECT_CALL(*writer, PersistedState) + .WillRepeatedly(Return(TestProtoObject())); + + return make_ready_future(make_status_or( + std::unique_ptr(std::move(writer)))); + }); + + auto client = AsyncClient(mock); + auto request = google::storage::v2::BidiWriteObjectRequest{}; + ASSERT_TRUE(TextFormat::ParseFromString(kExpectedRequest, &request)); + auto wt = client + .ResumeAppendableObjectUpload( + std::move(request), Options{} + .set>("O1-function") + .set>("O2-function")) + .get(); + ASSERT_STATUS_OK(wt); + AsyncWriter w; + AsyncToken t; + std::tie(w, t) = *std::move(wt); + EXPECT_TRUE(t.valid()); + EXPECT_THAT(w.PersistedState(), VariantWith( + IsProtoEqual(TestProtoObject()))); +} + GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_experimental } // namespace cloud From 49fa97f15c4da0719107a6aa280121f485dfcc2f Mon Sep 17 00:00:00 2001 From: rpodar Date: Fri, 5 Dec 2025 14:08:05 +0530 Subject: [PATCH 2/2] refactor: Consolidate `AsyncWriter` creation logic and reuse `ResumeAppendableObjectUpload` overload. --- google/cloud/storage/async/client.cc | 11 +---------- google/cloud/storage/async/client.h | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/google/cloud/storage/async/client.cc b/google/cloud/storage/async/client.cc index 088e893154659..392b6b12d835c 100644 --- a/google/cloud/storage/async/client.cc +++ b/google/cloud/storage/async/client.cc @@ -153,16 +153,7 @@ AsyncClient::ResumeAppendableObjectUpload(BucketName const& bucket_name, append_object_spec.set_object(std::move(object_name)); append_object_spec.set_generation(generation); - return connection_ - ->ResumeAppendableObjectUpload( - {std::move(request), - internal::MergeOptions(std::move(opts), connection_->options())}) - .then([](auto f) -> StatusOr> { - auto w = f.get(); - if (!w) return std::move(w).status(); - auto t = storage_internal::MakeAsyncToken(w->get()); - return std::make_pair(AsyncWriter(*std::move(w)), std::move(t)); - }); + return ResumeAppendableObjectUpload(std::move(request), std::move(opts)); } future>> diff --git a/google/cloud/storage/async/client.h b/google/cloud/storage/async/client.h index 7c8c0eb5c7bf2..6f33b51567c96 100644 --- a/google/cloud/storage/async/client.h +++ b/google/cloud/storage/async/client.h @@ -432,7 +432,7 @@ class AsyncClient { * @param bucket_name the name of the bucket that contains the object. * @param object_name the name of the object to be uploaded. * @param generation the object generation to be uploaded. - * @param opts options controlling the behaviour of this RPC, for example the + * @param opts options controlling the behavior of this RPC, for example the * application may change the retry policy. */ future>> @@ -446,7 +446,7 @@ class AsyncClient { * * @param request the request contents, it must include the bucket name, * object name, and generation. Many other fields are optional. - * @param opts options controlling the behaviour of this RPC, for example the + * @param opts options controlling the behavior of this RPC, for example the * application may change the retry policy. */ future>>