Repository: ClickHouse/ClickHouse
File: src/Disks/IO/AsynchronousBoundedReadBuffer.cpp (C++)

Code before:
    last_prefetch_info.submit_time = std::chrono::system_clock::now();
    last_prefetch_info.priority = priority;

    prefetch_buffer.resize(buffer_size);
    prefetch_future = readAsync(prefetch_buffer.data(), prefetch_buffer.size(), priority);
    ProfileEvents::increment(ProfileEvents::RemoteFSPrefetches);
}

void AsynchronousBoundedReadBuffer::setReadUntilPosition(size_t position)
{
    if (!read_until_position || position != *read_until_position)
    {
        if (position < file_offset_of_buffer_end)
        {
            /// file has been read beyond new read until position already
            if (available() >= file_offset_of_buffer_end - position)
            {
                /// new read until position is after the current position in the working buffer
                working_buffer.resize(working_buffer.size() - (file_offset_of_buffer_end - position));
                file_offset_of_buffer_end = position;
                pos = std::min(pos, working_buffer.end());
            }
            else
            {
                if (prefetch_future.valid())
                    prefetch_future.wait();
                /// new read until position is before the current position in the working buffer
                throw Exception(
                    ErrorCodes::LOGICAL_ERROR,
                    "Attempt to set read until position before already read data ({} < {}, info: {})",
                    position,
                    getPosition(),
                    impl->getInfoForLog());
            }
        }

        read_until_position = position;

        /// We must wait on future and reset the prefetch here, because otherwise there might be
        /// a race between reading the data in the threadpool and impl->setReadUntilPosition()
        /// which reinitializes internal remote read buffer (because if we have a new read range
        /// then we need a new range request) and in case of reading from cache we need to request
        /// and hold more file segment ranges from cache.
        resetPrefetch(FilesystemPrefetchState::CANCELLED_WITH_RANGE_CHANGE);
        impl->setReadUntilPosition(*read_until_position);
    }
}

void AsynchronousBoundedReadBuffer::appendToPrefetchLog(
    FilesystemPrefetchState state,
    int64_t size,

Review comment (bug):
When use_page_cache is true, prefetch_buffer is not resized (line 159-160), so prefetch_buffer.data() will be nullptr. This should pass nullptr explicitly when use_page_cache is true instead of relying on prefetch_buffer.data().
```suggestion
    prefetch_future = readAsync(use_page_cache ? nullptr : prefetch_buffer.data(), buffer_size, priority);
```

Code after:
        chassert(data != nullptr);
    IAsynchronousReader::Request request;
    request.descriptor = std::make_shared<RemoteFSFileDescriptor>(*impl, async_read_counters);
    request.buf = data;
    request.size = size;
    request.offset = file_offset_of_buffer_end;
    request.ignore = bytes_to_ignore;
    return reader.execute(request);
}

void AsynchronousBoundedReadBuffer::prefetch(Priority priority)
{
    if (prefetch_future.valid())
        return;

    if (!hasPendingDataToRead())
        return;

    last_prefetch_info.submit_time = std::chrono::system_clock::now();
    last_prefetch_info.priority = priority;

    /// Don't allocate any buffers if page cache is in use, the cache has its own buffers (PageCacheCell).
    if (!use_page_cache)
        prefetch_buffer.resize(buffer_size);

    prefetch_future = readAsync(prefetch_buffer.data(), buffer_size, priority);
    ProfileEvents::increment(ProfileEvents::RemoteFSPrefetches);
}

void AsynchronousBoundedReadBuffer::setReadUntilPosition(size_t position)
{
    if (!read_until_position || position != *read_until_position)
    {
        if (position < file_offset_of_buffer_end)
        {
            /// file has been read beyond new read until position already
            if (available() >= file_offset_of_buffer_end - position)
            {
                /// new read until position is after the current position in the working buffer
                working_buffer.resize(working_buffer.size() - (file_offset_of_buffer_end - position));
                file_offset_of_buffer_end = position;
                pos = std::min(pos, working_buffer.end());
            }
            else
            {
                if (prefetch_future.valid())
                    prefetch_future.wait();
                /// new read until position is before the current position in the working buffer
                throw Exception(
                    ErrorCodes::LOGICAL_ERROR,
                    "Attempt to set read until position before already read data ({} < {}, info: {})",