PBS-42 feature: Implement basic support for COM_BINLOG_DUMP packet handling (part 1) - #191
Conversation
| if (offset != 0 || length.has_value()) { | ||
| std::string range{"bytes=" + std::to_string(offset) + "-"}; | ||
| if (length.has_value()) { | ||
| range += std::to_string(offset + length.value() - 1); |
There was a problem hiding this comment.
What if offset == 0 and length == 0?
There was a problem hiding this comment.
Good point. Yes, we'd better make an early return in this case.
| "received memory content"); | ||
| } | ||
| } else { | ||
| if (content_length > max_memory_object_size) { |
There was a problem hiding this comment.
isn't stream_handler called after receiving the object from s3? So it is already in the memory.
There was a problem hiding this comment.
Yes, this is a compromise we had to make here. Unfortunately, there is no way in S3 API to "read no more than N bytes from an S3 object" in a single request. So, we either need to send another request (HeadObject) before this call, or simply rely on the fact that we won't be abusing this function.
See the TODO item above.
I don't like this either to be honest.
| std::uint64_t read_length{}; | ||
| if (length.has_value()) { | ||
| read_length = length.value(); | ||
| if (offset + read_length > file_size) { |
There was a problem hiding this comment.
overflow possible. use read_length > file_size - offset
There was a problem hiding this comment.
Yes, good point.
| public: | ||
| static constexpr std::size_t max_memory_object_size{1048576U}; | ||
| // 256 MB | ||
| static constexpr std::size_t max_memory_object_size{256UZ << 20UZ}; |
There was a problem hiding this comment.
we have the same in s3_storage_backend
There was a problem hiding this comment.
Will move this to basic_storage_backend.
832ca27 to
883fb21
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The S3 ranged-read path can materialize oversized responses before enforcing the memory limit.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Adds ranged object/file reads via a new util::byte_range abstraction to support upcoming binlog streaming functionality.
Changes:
- Adds byte-range validation and formatting.
- Extends filesystem and S3 storage APIs with ranged reads.
- Updates memory limits, callers, and related comments.
| File | Summary |
|---|---|
src/util/file_operations_helpers.hpp |
Declares ranged file reads. |
src/util/file_operations_helpers.cpp |
Implements ranged filesystem reads. |
src/util/byte_range.hpp |
Defines the byte-range API. |
src/util/byte_range.cpp |
Implements validation and formatting. |
src/util/byte_range_fwd.hpp |
Adds the forward declaration. |
src/binsrv/storage_core.hpp |
Corrects comments. |
src/binsrv/storage_core.cpp |
Corrects comments. |
src/binsrv/s3_storage_backend.hpp |
Updates the S3 backend interface. |
src/binsrv/s3_storage_backend.cpp |
Implements S3 ranged requests. |
src/binsrv/main_config.cpp |
Updates file-read usage. |
src/binsrv/keyring_record_collection.cpp |
Updates file-read usage. |
src/binsrv/filesystem_storage_backend.hpp |
Updates the filesystem backend interface. |
src/binsrv/filesystem_storage_backend.cpp |
Implements ranged filesystem access. |
src/binsrv/basic_storage_backend.hpp |
Extends the backend API and memory limit. |
src/binsrv/basic_storage_backend.cpp |
Forwards ranges to implementations. |
mtr/binlog_streaming/t/checkpointing.test |
Corrects a comment. |
extra/mysql_protocol/mysql/harness/stdx/ranges.h |
Corrects a comment. |
CMakeLists.txt |
Registers byte-range sources. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (range.has_length()) { | ||
| if (content_length != range.get_length()) { | ||
| util::exception_location().raise<std::out_of_range>( | ||
| "The requested S3 object range does not match the length of the " | ||
| "received memory content"); | ||
| } | ||
| } else { | ||
| if (content_length > max_memory_object_size) { | ||
| util::exception_location().raise<std::out_of_range>( | ||
| "S3 object is too large to be loaded in memory"); |
|
|
||
| [[nodiscard]] bool has_length() const noexcept { return length_.has_value(); } | ||
|
|
||
| [[nodiscard]] std::uint64_t get_length() const noexcept { |
There was a problem hiding this comment.
It will return 0 in both cases: if has length and it is 0 or it doesn't have length. That forces the caller to always check has_length() anyways. Wouldn't it be better to return optional?
There was a problem hiding this comment.
Yes, I just wanted it to be a simple accessor (with noexcept).
And instead of
byte_range br{...};
if (bt.get_length_opt().vas_value()) {
std::cout << bt.get_length_opt().value();
}
write something like
byte_range br{...};
if (bt.has_length()) {
std::cout << bt.get_length();
}
Although I understand that value_or() makes another unnecessary nullness check in my implementation.
| if (length_.has_value()) { | ||
| result += std::to_string(offset_ + *length_ - 1ULL); | ||
| } | ||
| return result; |
There was a problem hiding this comment.
It can be strange to get empty string, when is_full and on the other hand have an exception when is_empty
OK, I understand that it is used to form HTTP request, but in such a case it is rather an s3 class thing, not byte_range class responsibility. In other words, when I call to_string() I expect it to return meaningful string for logs for example, not a specific behavior that only s3 backend knows why
There was a problem hiding this comment.
Agree. If we extracted this byte_range into util as a generic reusable component, let's not bind us to S3 format.
…ndling (part 1) https://perconadev.atlassian.net/browse/PBS-42 * Introduced new 'util::byte_range' class for representing byte ranges in files. It supports empty ranges, full ranges, open-ended ranges, and specific byte ranges. * 'binsrv::basic_storage_backend' interface extended to support ranged reads (via optional parameter of type 'util::byte_range'). * Increased the size of max in-memory object supported by the 'get_object()' operation in both 's3' and 'filesystem' implementations of the 'binsrv::basic_storage_backend'. * Reworked 'util::read_file_content()' utility function to support ranged filesystem reads. * Fixed some typos in code comments.
883fb21 to
e06bdf2
Compare

https://perconadev.atlassian.net/browse/PBS-42
in files. It supports empty ranges, full ranges, open-ended ranges,
and specific byte ranges.
reads (via optional parameter of type 'util::byte_range').
'get_object()' operation in both 's3' and 'filesystem' implementations
of the 'binsrv::basic_storage_backend'.
ranged filesystem reads.