summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaksim Denisov <denisov.maksim@huawei.com>2024-09-17 10:20:55 +0200
committerMaksim Denisov <denisov.maksim@huawei.com>2024-09-18 09:45:19 +0200
commit60748d2914568e54f852281141f7c128c3ca1f2c (patch)
tree144ebe2871a46bc8b250892e3fd0ac948647763e /src
parent7d2f632b1dd1fe2ca01ef89716efe355e4d32687 (diff)
downloadjustbuild-60748d2914568e54f852281141f7c128c3ca1f2c.tar.gz
Implement ByteStreamUtils::ReadRequest class
...and remove split serialization/deserialization implementations.
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/execution_api/common/TARGETS3
-rw-r--r--src/buildtool/execution_api/common/bytestream_utils.cpp104
-rw-r--r--src/buildtool/execution_api/common/bytestream_utils.hpp40
-rw-r--r--src/buildtool/execution_api/execution_service/bytestream_server.cpp10
-rw-r--r--src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp14
-rw-r--r--src/buildtool/execution_api/remote/bazel/bytestream_client.hpp18
6 files changed, 165 insertions, 24 deletions
diff --git a/src/buildtool/execution_api/common/TARGETS b/src/buildtool/execution_api/common/TARGETS
index a060d0fa..19363342 100644
--- a/src/buildtool/execution_api/common/TARGETS
+++ b/src/buildtool/execution_api/common/TARGETS
@@ -34,6 +34,9 @@
{ "type": ["@", "rules", "CC", "library"]
, "name": ["bytestream_utils"]
, "hdrs": ["bytestream_utils.hpp"]
+ , "srcs": ["bytestream_utils.cpp"]
+ , "private-deps":
+ [["@", "fmt", "", "fmt"], ["src/buildtool/common", "bazel_types"]]
, "stage": ["src", "buildtool", "execution_api", "common"]
}
, "api_bundle":
diff --git a/src/buildtool/execution_api/common/bytestream_utils.cpp b/src/buildtool/execution_api/common/bytestream_utils.cpp
new file mode 100644
index 00000000..cd1f0d19
--- /dev/null
+++ b/src/buildtool/execution_api/common/bytestream_utils.cpp
@@ -0,0 +1,104 @@
+// Copyright 2024 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/buildtool/execution_api/common/bytestream_utils.hpp"
+
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "fmt/core.h"
+#include "src/buildtool/common/bazel_types.hpp"
+
+namespace {
+/// \brief Split a string into parts with '/' delimiter
+/// \param request String to be split
+/// \return A vector of parts on success or an empty vector on failure.
+[[nodiscard]] auto SplitRequest(std::string const& request) noexcept
+ -> std::vector<std::string_view> {
+ std::vector<std::string_view> parts;
+ try {
+ std::size_t shift = 0;
+ for (std::size_t length = 0; shift + length < request.size();
+ ++length) {
+ if (request.at(shift + length) == '/') {
+ parts.emplace_back(request.data() + shift, length);
+ shift += length + 1;
+ length = 0;
+ }
+ }
+
+ if (shift < request.size()) {
+ parts.emplace_back(request.data() + shift, request.size() - shift);
+ }
+ } catch (...) {
+ return {};
+ }
+ return parts;
+}
+
+[[nodiscard]] inline auto ToBazelDigest(std::string hash,
+ std::int64_t size) noexcept
+ -> bazel_re::Digest {
+ bazel_re::Digest digest{};
+ digest.set_hash(std::move(hash));
+ digest.set_size_bytes(size);
+ return digest;
+}
+} // namespace
+
+ByteStreamUtils::ReadRequest::ReadRequest(
+ std::string instance_name,
+ bazel_re::Digest const& digest) noexcept
+ : instance_name_{std::move(instance_name)},
+ hash_{digest.hash()},
+ size_{digest.size_bytes()} {}
+
+auto ByteStreamUtils::ReadRequest::ToString() && noexcept -> std::string {
+ return fmt::format("{}/{}/{}/{}",
+ std::move(instance_name_),
+ ByteStreamUtils::kBlobs,
+ std::move(hash_),
+ size_);
+}
+
+auto ByteStreamUtils::ReadRequest::FromString(
+ std::string const& request) noexcept -> std::optional<ReadRequest> {
+ static constexpr std::size_t kInstanceNameIndex = 0U;
+ static constexpr std::size_t kBlobsIndex = 1U;
+ static constexpr std::size_t kHashIndex = 2U;
+ static constexpr std::size_t kSizeIndex = 3U;
+ static constexpr std::size_t kReadRequestPartsCount = 4U;
+
+ auto const parts = ::SplitRequest(request);
+ if (parts.size() != kReadRequestPartsCount or
+ parts.at(kBlobsIndex).compare(ByteStreamUtils::kBlobs) != 0) {
+ return std::nullopt;
+ }
+
+ ReadRequest result;
+ result.instance_name_ = std::string(parts.at(kInstanceNameIndex));
+ result.hash_ = std::string(parts.at(kHashIndex));
+ try {
+ result.size_ = std::stoi(std::string(parts.at(kSizeIndex)));
+ } catch (...) {
+ return std::nullopt;
+ }
+ return result;
+}
+
+auto ByteStreamUtils::ReadRequest::GetDigest() const noexcept
+ -> bazel_re::Digest {
+ return ToBazelDigest(hash_, size_);
+}
diff --git a/src/buildtool/execution_api/common/bytestream_utils.hpp b/src/buildtool/execution_api/common/bytestream_utils.hpp
index c28c9627..9b46073f 100644
--- a/src/buildtool/execution_api/common/bytestream_utils.hpp
+++ b/src/buildtool/execution_api/common/bytestream_utils.hpp
@@ -16,11 +16,51 @@
#define INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_BYTESTREAM_UTILS_HPP
#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+
+namespace build::bazel::remote::execution::v2 {
+class Digest;
+}
+namespace bazel_re = build::bazel::remote::execution::v2;
class ByteStreamUtils final {
+ static constexpr auto* kBlobs = "blobs";
+
public:
// Chunk size for uploads (default size used by BuildBarn)
static constexpr std::size_t kChunkSize = 64 * 1024;
+
+ /// \brief Create a read request for the bytestream service to be
+ /// transferred over the net. Handles serialization/deserialization on its
+ /// own. The pattern is:
+ /// "{instance_name}/{kBlobs}/{digest.hash()}/{digest.size_bytes()}".
+ /// "instance_name_example/blobs/62183d7a696acf7e69e218efc82c93135f8c85f895/4424712"
+ class ReadRequest final {
+ public:
+ explicit ReadRequest(std::string instance_name,
+ bazel_re::Digest const& digest) noexcept;
+
+ [[nodiscard]] auto ToString() && noexcept -> std::string;
+
+ [[nodiscard]] static auto FromString(
+ std::string const& request) noexcept -> std::optional<ReadRequest>;
+
+ [[nodiscard]] auto GetInstanceName() const noexcept
+ -> std::string const& {
+ return instance_name_;
+ }
+
+ [[nodiscard]] auto GetDigest() const noexcept -> bazel_re::Digest;
+
+ private:
+ std::string instance_name_;
+ std::string hash_;
+ std::int64_t size_ = 0;
+
+ ReadRequest() = default;
+ };
};
#endif // INCLUDED_SRC_BUILDTOOL_EXECUTION_API_COMMON_BYTESTREAM_UTILS_HPP
diff --git a/src/buildtool/execution_api/execution_service/bytestream_server.cpp b/src/buildtool/execution_api/execution_service/bytestream_server.cpp
index 985f41a9..789fdf95 100644
--- a/src/buildtool/execution_api/execution_service/bytestream_server.cpp
+++ b/src/buildtool/execution_api/execution_service/bytestream_server.cpp
@@ -63,18 +63,16 @@ auto BytestreamServiceImpl::Read(
::grpc::ServerWriter<::google::bytestream::ReadResponse>* writer)
-> ::grpc::Status {
logger_.Emit(LogLevel::Trace, "Read {}", request->resource_name());
- // resource_name is of type
- // remote-execution/blobs/62f408d64bca5de775c4b1dbc3288fc03afd6b19eb/0
- auto const digest = ParseResourceName(request->resource_name());
- if (not digest) {
+ auto const read_request =
+ ByteStreamUtils::ReadRequest::FromString(request->resource_name());
+ if (not read_request) {
auto const str =
fmt::format("could not parse {}", request->resource_name());
logger_.Emit(LogLevel::Error, "{}", str);
return ::grpc::Status{::grpc::StatusCode::INVALID_ARGUMENT, str};
}
-
auto const read_digest = ArtifactDigestFactory::FromBazel(
- storage_config_.hash_function.GetType(), *digest);
+ storage_config_.hash_function.GetType(), read_request->GetDigest());
if (not read_digest) {
logger_.Emit(LogLevel::Debug, "{}", read_digest.error());
return ::grpc::Status{::grpc::StatusCode::INVALID_ARGUMENT,
diff --git a/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp b/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp
index 1516ea0c..ea248810 100644
--- a/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp
+++ b/src/buildtool/execution_api/remote/bazel/bazel_cas_client.cpp
@@ -28,6 +28,7 @@
#include "src/buildtool/common/remote/client_common.hpp"
#include "src/buildtool/common/remote/retry.hpp"
#include "src/buildtool/common/remote/retry_config.hpp"
+#include "src/buildtool/execution_api/common/bytestream_utils.hpp"
#include "src/buildtool/execution_api/common/execution_common.hpp"
#include "src/buildtool/execution_api/common/message_limits.hpp"
#include "src/buildtool/file_system/object_type.hpp"
@@ -35,13 +36,6 @@
namespace {
-[[nodiscard]] auto ToResourceName(std::string const& instance_name,
- bazel_re::Digest const& digest) noexcept
- -> std::string {
- return fmt::format(
- "{}/blobs/{}/{}", instance_name, digest.hash(), digest.size_bytes());
-}
-
// In order to determine whether blob splitting is supported at the remote, a
// trial request to the remote CAS service is issued. This is just a workaround
// until the blob split API extension is accepted as part of the official remote
@@ -341,13 +335,15 @@ auto BazelCasClient::UpdateSingleBlob(std::string const& instance_name,
auto BazelCasClient::IncrementalReadSingleBlob(std::string const& instance_name,
bazel_re::Digest const& digest)
const noexcept -> ByteStreamClient::IncrementalReader {
- return stream_->IncrementalRead(ToResourceName(instance_name, digest));
+ return stream_->IncrementalRead(
+ ByteStreamUtils::ReadRequest{instance_name, digest});
}
auto BazelCasClient::ReadSingleBlob(
std::string const& instance_name,
bazel_re::Digest const& digest) const noexcept -> std::optional<BazelBlob> {
- if (auto data = stream_->Read(ToResourceName(instance_name, digest))) {
+ if (auto data = stream_->Read(
+ ByteStreamUtils::ReadRequest{instance_name, digest})) {
return BazelBlob{digest, std::move(*data), /*is_exec=*/false};
}
return std::nullopt;
diff --git a/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp b/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp
index a444e61b..02538384 100644
--- a/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp
+++ b/src/buildtool/execution_api/remote/bazel/bytestream_client.hpp
@@ -70,11 +70,11 @@ class ByteStreamClient {
IncrementalReader(
gsl::not_null<google::bytestream::ByteStream::Stub*> const& stub,
- Logger const* logger,
- std::string const& resource_name)
+ ByteStreamUtils::ReadRequest&& read_request,
+ Logger const* logger)
: logger_{logger} {
google::bytestream::ReadRequest request{};
- request.set_resource_name(resource_name);
+ request.set_resource_name(std::move(read_request).ToString());
reader_ = stub->Read(&ctx_, request);
}
};
@@ -86,14 +86,14 @@ class ByteStreamClient {
CreateChannelWithCredentials(server, port, auth));
}
- [[nodiscard]] auto IncrementalRead(
- std::string const& resource_name) const noexcept -> IncrementalReader {
- return IncrementalReader{stub_.get(), &logger_, resource_name};
+ [[nodiscard]] auto IncrementalRead(ByteStreamUtils::ReadRequest&& request)
+ const noexcept -> IncrementalReader {
+ return IncrementalReader{stub_.get(), std::move(request), &logger_};
}
- [[nodiscard]] auto Read(std::string const& resource_name) const noexcept
- -> std::optional<std::string> {
- auto reader = IncrementalRead(resource_name);
+ [[nodiscard]] auto Read(ByteStreamUtils::ReadRequest&& request)
+ const noexcept -> std::optional<std::string> {
+ auto reader = IncrementalRead(std::move(request));
std::string output{};
auto data = reader.Next();
while (data and not data->empty()) {