Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ReadBuffer for AzureBlobStorage #61884

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft

Conversation

SmitaRKulkarni
Copy link
Member

@SmitaRKulkarni SmitaRKulkarni commented Mar 25, 2024

Changelog category (leave one):

  • Performance Improvement

Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):

Updated to use azure api DownloadTo instead of Download and ReadTo apis. DownloadTo reads to a buffer and uses parallel requests.

@SmitaRKulkarni SmitaRKulkarni marked this pull request as draft March 25, 2024 18:30
@robot-clickhouse-ci-1 robot-clickhouse-ci-1 added the pr-performance Pull request with some performance improvements label Mar 25, 2024
@robot-clickhouse-ci-1
Copy link
Contributor

robot-clickhouse-ci-1 commented Mar 25, 2024

This is an automated comment for commit fa4d205 with description of existing statuses. It's updated for the latest CI running

❌ Click here to open a full report in a separate page

Check nameDescriptionStatus
CI runningA meta-check that indicates the running CI. Normally, it's in success or pending state. The failed status indicates some problems with the PR⏳ pending
ClickHouse build checkBuilds ClickHouse in various configurations for use in further steps. You have to fix the builds that fail. Build logs often has enough information to fix the error, but you might have to reproduce the failure locally. The cmake options can be found in the build log, grepping for cmake. Use these options and follow the general build process⏳ pending
Integration testsThe integration tests report. In parenthesis the package type is given, and in square brackets are the optional part/total tests❌ failure
Mergeable CheckChecks if all other necessary checks are successful❌ failure
Performance ComparisonMeasure changes in query performance. The performance test report is described in detail here. In square brackets are the optional part/total tests❌ failure
Successful checks
Check nameDescriptionStatus
A SyncThere's no description for the check yet, please add it to tests/ci/ci_config.py:CHECK_DESCRIPTIONS✅ success
AST fuzzerRuns randomly generated queries to catch program errors. The build type is optionally given in parenthesis. If it fails, ask a maintainer for help✅ success
ClickBenchRuns [ClickBench](https://github.com/ClickHouse/ClickBench/) with instant-attach table✅ success
Compatibility checkChecks that clickhouse binary runs on distributions with old libc versions. If it fails, ask a maintainer for help✅ success
Docker keeper imageThe check to build and optionally push the mentioned image to docker hub✅ success
Docker server imageThe check to build and optionally push the mentioned image to docker hub✅ success
Docs checkBuilds and tests the documentation✅ success
Fast testNormally this is the first check that is ran for a PR. It builds ClickHouse and runs most of stateless functional tests, omitting some. If it fails, further checks are not started until it is fixed. Look at the report to see which tests fail, then reproduce the failure locally as described here✅ success
Flaky testsChecks if new added or modified tests are flaky by running them repeatedly, in parallel, with more randomization. Functional tests are run 100 times with address sanitizer, and additional randomization of thread scheduling. Integrational tests are run up to 10 times. If at least once a new test has failed, or was too long, this check will be red. We don't allow flaky tests, read the doc✅ success
Install packagesChecks that the built packages are installable in a clear environment✅ success
PR CheckThere's no description for the check yet, please add it to tests/ci/ci_config.py:CHECK_DESCRIPTIONS✅ success
Stateful testsRuns stateful functional tests for ClickHouse binaries built in various configurations -- release, debug, with sanitizers, etc✅ success
Stateless testsRuns stateless functional tests for ClickHouse binaries built in various configurations -- release, debug, with sanitizers, etc✅ success
Stress testRuns stateless functional tests concurrently from several clients to detect concurrency-related errors✅ success
Style checkRuns a set of checks to keep the code style clean. If some of tests failed, see the related log from the report✅ success
Unit testsRuns the unit tests for different release types✅ success
Upgrade checkRuns stress tests on server version from last release and then tries to upgrade it to the version from the PR. It checks if the new server can successfully startup without any errors, crashes or sanitizer asserts✅ success

@kssenii kssenii self-assigned this Mar 25, 2024
@@ -50,14 +50,12 @@ class ReadBufferFromAzureBlobStorage : public ReadBufferFromFileBase

private:

void initialize();
size_t readBytes();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed this function, as it also reads data now. But we still need initialized bool for other checks, so kept that as it is.

@SmitaRKulkarni SmitaRKulkarni marked this pull request as ready for review March 26, 2024 10:43

std::unique_ptr<Azure::Core::IO::BodyStream> body_stream = std::move(download_response.Value.BodyStream);
bytes_copied = body_stream->ReadToCount(reinterpret_cast<uint8_t *>(to), body_stream->Length());
auto download_response = blob_client->DownloadTo(reinterpret_cast<uint8_t *>(to), n, download_options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it really give a performance improvement? Seems like just a different api (and this new way with DownloadTo is better and more clear, so the change is good) but it seems to be not to be improving the perf anyhow.

Copy link
Member

@kssenii kssenii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but may be worth to update changelog entry to not-for-changelog, or if not updating then to make a bit more coherent description of the change in the changelog entry.

size_t bytes_read = 0;

size_t sleep_time_with_backoff_milliseconds = 100;
if (static_cast<size_t>(offset) >= getFileSize())
Copy link
Member

@kssenii kssenii Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a azure disk read it will be better not to execute additional request to azure just to get file size (getFileSize is only used for external table engines) as it is already known before we create the read buffer. So may be just pass file_size as optional argument in constructor of ReadBufferFrpmAzureBlobStorage?

btw why do we need this check? There is no such check for other buffers AFAIK

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For other buffers, I see we send the request to read the full intended length of the file.
Here we are using readBytes function to read bytes to a buffer and only read as much as its capacity (data_capacity). So every time we read we extend the offset at one point it will reach intended size or file_size .

In getFileSize() function we only read the value once, it is just returned for future calls.

Please let me know if you have any suggestions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In getFileSize() function we only read the value once, it is just returned for future calls.

Yes, I saw that it is fetched only once, but if it is avoidable - better to avoid, we already have a file size when we create this read buffer, better to use it.

For other buffers, I see we send the request to read the full intended length of the file.

But isn't it the same as here? Here we have a buffer and try to read data_capacity size to it, but if we reach the end of file while reading into buffer - we will just read nothing and the buffer will be empty, so we return false as well. Or am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But isn't it the same as here? Here we have a buffer and try to read data_capacity size to it, but if we reach the end of file while reading into buffer - we will just read nothing and the buffer will be empty, so we return false as well. Or am I missing something?

DownloadTo function takes Range as a parameter (offset, length) if length is zero it reads full file, so when we reach end of file, the range will be (offset=file_size, length), even if length is zero we see error message that the "The range specified is invalid for the current size of the resource".
Previously when we used to Download & then ReadToBuffer, there we had a check to_read_bytes = min(total_size-offset, length), and we would just read 0 bytes & return false.
Hope that clarifies the need. Please suggest if you have better options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok understood. Also what if there are remaining N bytes to read from file and N is less that buffer_capacity, shouldn't it fail as well with incorrect range error? Seems currently code is not protected against this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But read_until_position is not always set. It is not set for remote_filesystem_read_method=read

for (size_t i = 0; i < max_single_download_retries; ++i)
{
try
{
auto download_response = blob_client->Download(download_options);
data_stream = std::move(download_response.Value.BodyStream);
auto download_response = blob_client->DownloadTo(reinterpret_cast<uint8_t *>(data_ptr), data_capacity, download_options);
Copy link
Member

@CurtizJ CurtizJ Apr 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will not improve performance but will decrease it instead because it will make a GET request for each call of nextImpl instead of one request in initialize method, as I understand the azure sdk. DownloadTo looks more suitable for reading small blobs that can be read with one call.

@SmitaRKulkarni SmitaRKulkarni marked this pull request as draft April 10, 2024 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr-performance Pull request with some performance improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants