Skip to content

Commit ee6159e

Browse files
authored
fix: handle non-contiguous RowRanges when resolving global row IDs (#383)
1 parent 25a1294 commit ee6159e

33 files changed

Lines changed: 691 additions & 227 deletions

include/paimon/reader/file_batch_reader.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@ class PAIMON_EXPORT FileBatchReader : public BatchReader {
4646
using BatchReader::NextBatch;
4747
using BatchReader::NextBatchWithBitmap;
4848

49-
/// Get the row number of the first row in the previously read batch.
50-
virtual Result<uint64_t> GetPreviousBatchFirstRowNumber() const = 0;
49+
/// Get the file-level row ID for a given batch-relative row index
50+
/// in the previously read batch.
51+
///
52+
/// @param batch_row_id Zero-based index within the current batch.
53+
/// @return The corresponding file-level row ID, or Status::Invalid
54+
/// if no batch has been read yet, the last batch was EOF,
55+
/// or batch_row_id is out of range.
56+
virtual Result<uint64_t> GetPreviousBatchFileRowId(uint64_t batch_row_id) const = 0;
5157

5258
/// Get the number of rows in the file.
5359
virtual Result<uint64_t> GetNumberOfRows() const = 0;

src/paimon/common/data/shredding/map_shared_shredding_file_reader.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,9 @@ void MapSharedShreddingFileReader::Close() {
391391
reader_->Close();
392392
}
393393

394-
Result<uint64_t> MapSharedShreddingFileReader::GetPreviousBatchFirstRowNumber() const {
395-
return reader_->GetPreviousBatchFirstRowNumber();
394+
Result<uint64_t> MapSharedShreddingFileReader::GetPreviousBatchFileRowId(
395+
uint64_t batch_row_id) const {
396+
return reader_->GetPreviousBatchFileRowId(batch_row_id);
396397
}
397398

398399
Result<uint64_t> MapSharedShreddingFileReader::GetNumberOfRows() const {

src/paimon/common/data/shredding/map_shared_shredding_file_reader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class MapSharedShreddingFileReader : public FileBatchReader {
6060

6161
void Close() override;
6262

63-
Result<uint64_t> GetPreviousBatchFirstRowNumber() const override;
63+
Result<uint64_t> GetPreviousBatchFileRowId(uint64_t batch_row_id) const override;
6464

6565
Result<uint64_t> GetNumberOfRows() const override;
6666

src/paimon/common/file_index/bitmap/apply_bitmap_index_batch_reader.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ class ApplyBitmapIndexBatchReader : public FileBatchReader {
8080
return Status::Invalid("ApplyBitmapIndexBatchReader does not support SetReadSchema");
8181
}
8282

83-
Result<uint64_t> GetPreviousBatchFirstRowNumber() const override {
84-
return reader_->GetPreviousBatchFirstRowNumber();
83+
Result<uint64_t> GetPreviousBatchFileRowId(uint64_t batch_row_id) const override {
84+
return reader_->GetPreviousBatchFileRowId(batch_row_id);
8585
}
8686

8787
Result<uint64_t> GetNumberOfRows() const override {
@@ -94,14 +94,23 @@ class ApplyBitmapIndexBatchReader : public FileBatchReader {
9494

9595
private:
9696
Result<RoaringBitmap32> Filter(int32_t batch_size) const {
97-
RoaringBitmap32 is_valid;
98-
PAIMON_ASSIGN_OR_RAISE(int32_t start_pos, reader_->GetPreviousBatchFirstRowNumber());
99-
int32_t length = batch_size;
100-
for (auto iter = bitmap_.EqualOrLarger(start_pos);
101-
iter != bitmap_.End() && *iter < start_pos + length; ++iter) {
102-
is_valid.Add(*iter - start_pos);
97+
RoaringBitmap32 result;
98+
auto bitmap_iter = bitmap_.Begin();
99+
auto bitmap_end = bitmap_.End();
100+
101+
for (int32_t i = 0; i < batch_size; ++i) {
102+
PAIMON_ASSIGN_OR_RAISE(uint64_t file_row_id, reader_->GetPreviousBatchFileRowId(i));
103+
while (bitmap_iter != bitmap_end && static_cast<uint64_t>(*bitmap_iter) < file_row_id) {
104+
++bitmap_iter;
105+
}
106+
if (bitmap_iter == bitmap_end) {
107+
break;
108+
}
109+
if (static_cast<uint64_t>(*bitmap_iter) == file_row_id) {
110+
result.Add(i);
111+
}
103112
}
104-
return is_valid;
113+
return result;
105114
}
106115

107116
private:

src/paimon/common/reader/delegating_prefetch_reader.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class DelegatingPrefetchReader : public FileBatchReader {
5454
return prefetch_reader_->SetReadSchema(read_schema, predicate, selection_bitmap);
5555
}
5656

57-
Result<uint64_t> GetPreviousBatchFirstRowNumber() const override {
58-
return GetReader()->GetPreviousBatchFirstRowNumber();
57+
Result<uint64_t> GetPreviousBatchFileRowId(uint64_t batch_row_id) const override {
58+
return GetReader()->GetPreviousBatchFileRowId(batch_row_id);
5959
}
6060

6161
Result<uint64_t> GetNumberOfRows() const override {

src/paimon/common/reader/prefetch_file_batch_reader_impl.cpp

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ class Schema;
4040

4141
namespace paimon {
4242

43+
namespace {
44+
45+
std::pair<int64_t, int64_t> ComputeBatchSliceByReadRange(
46+
const std::vector<uint64_t>& global_row_ids, const std::pair<uint64_t, uint64_t>& read_range) {
47+
auto begin_it =
48+
std::lower_bound(global_row_ids.begin(), global_row_ids.end(), read_range.first);
49+
auto end_it = std::lower_bound(global_row_ids.begin(), global_row_ids.end(), read_range.second);
50+
return {static_cast<int64_t>(std::distance(global_row_ids.begin(), begin_it)),
51+
static_cast<int64_t>(std::distance(global_row_ids.begin(), end_it))};
52+
}
53+
54+
} // namespace
55+
4356
Result<std::unique_ptr<PrefetchFileBatchReaderImpl>> PrefetchFileBatchReaderImpl::Create(
4457
const std::string& data_file_path, const ReaderBuilder* reader_builder,
4558
const std::shared_ptr<FileSystem>& fs, uint32_t prefetch_max_parallel_num, int32_t batch_size,
@@ -265,6 +278,7 @@ Status PrefetchFileBatchReaderImpl::CleanUp() {
265278

266279
read_ranges_.clear();
267280
read_ranges_in_group_.clear();
281+
current_batch_global_row_ids_.clear();
268282
clean_prefetch_queue();
269283
for (size_t i = 0; i < readers_pos_.size(); i++) {
270284
readers_pos_[i]->store(0);
@@ -409,28 +423,44 @@ Status PrefetchFileBatchReaderImpl::EnsureReaderPosition(
409423
Status PrefetchFileBatchReaderImpl::HandleReadResult(
410424
size_t reader_idx, const std::pair<uint64_t, uint64_t>& read_range,
411425
ReadBatchWithBitmap&& read_batch_with_bitmap) {
412-
PAIMON_ASSIGN_OR_RAISE(uint64_t first_row_number,
413-
readers_[reader_idx]->GetPreviousBatchFirstRowNumber());
414426
auto& prefetch_queue = prefetch_queues_[reader_idx];
415427
if (!BatchReader::IsEofBatch(read_batch_with_bitmap)) {
416428
auto& [read_batch, bitmap] = read_batch_with_bitmap;
417429
auto& [c_array, c_schema] = read_batch;
430+
std::vector<uint64_t> global_row_ids;
431+
global_row_ids.reserve(c_array->length);
432+
for (int64_t i = 0; i < c_array->length; ++i) {
433+
PAIMON_ASSIGN_OR_RAISE(uint64_t global_row_id,
434+
readers_[reader_idx]->GetPreviousBatchFileRowId(i));
435+
global_row_ids.push_back(global_row_id);
436+
}
437+
if (global_row_ids.empty()) {
438+
ReaderUtils::ReleaseReadBatch(std::move(read_batch));
439+
return Status::OK();
440+
}
441+
auto [slice_begin, slice_end] = ComputeBatchSliceByReadRange(global_row_ids, read_range);
442+
// slice_begin should always be 0, records before read_range.first have been consumed or
443+
// filtered out.
444+
if (slice_begin != 0) {
445+
return Status::Invalid(fmt::format("Slice begin is {}, which is not 0.", slice_begin));
446+
}
418447

419-
if (first_row_number >= read_range.second) {
420-
// fully out of range, data before first_row_number has been filtered out
421-
readers_pos_[reader_idx]->store(first_row_number);
448+
if (0 == slice_end) {
449+
// fully out of range, data before global_row_ids has been filtered out
450+
readers_pos_[reader_idx]->store(global_row_ids[0]);
422451
ReaderUtils::ReleaseReadBatch(std::move(read_batch));
423452
return Status::OK();
424-
} else if (first_row_number + c_array->length > read_range.second) {
453+
} else if (slice_end < c_array->length) {
425454
// partially out of range, data before read_range.second has been effectively consumed
426455
readers_pos_[reader_idx]->store(read_range.second);
427456
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Array> src_array,
428457
arrow::ImportArray(c_array.get(), c_schema.get()));
429-
int32_t target_length = read_range.second - first_row_number;
430-
auto array = src_array->Slice(/*offset=*/0, target_length);
458+
auto array = src_array->Slice(0, slice_end);
431459
PAIMON_RETURN_NOT_OK_FROM_ARROW(
432460
arrow::ExportArray(*array, c_array.get(), c_schema.get()));
433-
bitmap.RemoveRange(target_length, src_array->length());
461+
bitmap.RemoveRange(slice_end, src_array->length());
462+
global_row_ids =
463+
std::vector<uint64_t>(global_row_ids.begin(), global_row_ids.begin() + slice_end);
434464
} else {
435465
// all within the range, data before readers_[reader_idx]->GetNextRowToRead() has been
436466
// effectively consumed
@@ -440,11 +470,12 @@ Status PrefetchFileBatchReaderImpl::HandleReadResult(
440470
ReaderUtils::ReleaseReadBatch(std::move(read_batch));
441471
return Status::OK();
442472
}
443-
prefetch_queue->push({read_range, std::move(read_batch_with_bitmap), first_row_number});
473+
prefetch_queue->push(
474+
{read_range, std::move(read_batch_with_bitmap), std::move(global_row_ids)});
444475
} else {
445476
std::pair<uint64_t, uint64_t> eof_range;
446477
PAIMON_ASSIGN_OR_RAISE(eof_range, EofRange());
447-
prefetch_queue->push({eof_range, std::move(read_batch_with_bitmap), first_row_number});
478+
prefetch_queue->push({eof_range, std::move(read_batch_with_bitmap), {}});
448479
readers_pos_[reader_idx]->store(std::numeric_limits<uint64_t>::max());
449480
}
450481
return Status::OK();
@@ -527,7 +558,7 @@ Result<BatchReader::ReadBatchWithBitmap> PrefetchFileBatchReaderImpl::NextBatchW
527558
std::unique_lock<std::mutex> lock(working_mutex_);
528559
cv_.notify_one();
529560
}
530-
previous_batch_first_row_num_ = prefetch_batch.value().previous_batch_first_row_num;
561+
current_batch_global_row_ids_ = std::move(prefetch_batch.value().global_row_ids);
531562
return std::move(prefetch_batch).value().batch;
532563
}
533564
}
@@ -537,7 +568,7 @@ Result<BatchReader::ReadBatchWithBitmap> PrefetchFileBatchReaderImpl::NextBatchW
537568
assert(false);
538569
return Status::Invalid("peek batch not suppose to be nullptr");
539570
}
540-
previous_batch_first_row_num_ = peek_batch->previous_batch_first_row_num;
571+
current_batch_global_row_ids_.clear();
541572
return BatchReader::MakeEofBatchWithBitmap();
542573
}
543574
if (value_count == prefetch_queues_.size()) {
@@ -571,8 +602,19 @@ Result<std::unique_ptr<::ArrowSchema>> PrefetchFileBatchReaderImpl::GetFileSchem
571602
return readers_[0]->GetFileSchema();
572603
}
573604

574-
Result<uint64_t> PrefetchFileBatchReaderImpl::GetPreviousBatchFirstRowNumber() const {
575-
return previous_batch_first_row_num_;
605+
Result<uint64_t> PrefetchFileBatchReaderImpl::GetPreviousBatchFileRowId(
606+
uint64_t batch_row_id) const {
607+
if (current_batch_global_row_ids_.empty()) {
608+
return Status::Invalid(
609+
"Last batch is not read or last batch is empty, cannot get previous batch global row "
610+
"id");
611+
}
612+
if (batch_row_id >= current_batch_global_row_ids_.size()) {
613+
return Status::Invalid(
614+
fmt::format("batch_row_id {} is out of range, last batch row count is {}", batch_row_id,
615+
current_batch_global_row_ids_.size()));
616+
}
617+
return current_batch_global_row_ids_[batch_row_id];
576618
}
577619

578620
Result<uint64_t> PrefetchFileBatchReaderImpl::GetNumberOfRows() const {

src/paimon/common/reader/prefetch_file_batch_reader_impl.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class PrefetchFileBatchReaderImpl : public PrefetchFileBatchReader {
7676
const std::optional<RoaringBitmap32>& selection_bitmap) override;
7777

7878
Status SeekToRow(uint64_t row_number) override;
79-
Result<uint64_t> GetPreviousBatchFirstRowNumber() const override;
79+
Result<uint64_t> GetPreviousBatchFileRowId(uint64_t batch_row_id) const override;
8080
Result<uint64_t> GetNumberOfRows() const override;
8181
uint64_t GetNextRowToRead() const override;
8282
void Close() override;
@@ -105,7 +105,7 @@ class PrefetchFileBatchReaderImpl : public PrefetchFileBatchReader {
105105
struct PrefetchBatch {
106106
std::pair<uint64_t, uint64_t> read_range;
107107
BatchReader::ReadBatchWithBitmap batch;
108-
uint64_t previous_batch_first_row_num;
108+
std::vector<uint64_t> global_row_ids;
109109
};
110110

111111
PrefetchFileBatchReaderImpl(
@@ -160,7 +160,7 @@ class PrefetchFileBatchReaderImpl : public PrefetchFileBatchReader {
160160
std::unique_ptr<std::thread> background_thread_;
161161
Status read_status_;
162162
std::atomic<bool> is_shutdown_ = false;
163-
uint64_t previous_batch_first_row_num_ = std::numeric_limits<uint64_t>::max();
163+
std::vector<uint64_t> current_batch_global_row_ids_;
164164
bool need_prefetch_ = false;
165165
bool read_ranges_freshed_ = false;
166166
const uint32_t prefetch_queue_capacity_;

0 commit comments

Comments
 (0)