Skip to content

Commit d688d39

Browse files
authored
feat(global-index): add bitmap index format (#352)
1 parent 77bc819 commit d688d39

14 files changed

Lines changed: 2319 additions & 2 deletions

‎src/paimon/CMakeLists.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ if(PAIMON_BUILD_TESTS)
617617
common/global_index/bitmap_global_index_result_test.cpp
618618
common/global_index/bitmap_scored_global_index_result_test.cpp
619619
common/global_index/bitmap/bitmap_global_index_test.cpp
620+
common/global_index/bitmap/bitmap_global_index_format_test.cpp
621+
common/global_index/bitmap/bitmap_index_reader_test.cpp
620622
common/global_index/sorted_index_file_meta_test.cpp
621623
common/global_index/btree/btree_file_footer_test.cpp
622624
common/global_index/key_serializer_test.cpp

‎src/paimon/common/global_index/CMakeLists.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
set(PAIMON_GLOBAL_INDEX_SRC
1818
bitmap/bitmap_global_index.cpp
19+
bitmap/bitmap_global_index_format.cpp
20+
bitmap/bitmap_global_index_writer.cpp
21+
bitmap/bitmap_index_reader.cpp
1922
bitmap/bitmap_global_index_factory.cpp
2023
btree/btree_file_footer.cpp
2124
btree/btree_global_index_factory.cpp

‎src/paimon/common/global_index/bitmap/bitmap_global_index_format.cpp‎

Lines changed: 583 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
#include <cstdint>
23+
#include <memory>
24+
#include <utility>
25+
#include <vector>
26+
27+
#include "paimon/common/compression/block_compression_factory.h"
28+
#include "paimon/common/memory/memory_slice.h"
29+
#include "paimon/fs/file_system.h"
30+
#include "paimon/memory/bytes.h"
31+
#include "paimon/memory/memory_pool.h"
32+
#include "paimon/result.h"
33+
#include "paimon/status.h"
34+
#include "paimon/utils/roaring_bitmap64.h"
35+
36+
namespace paimon {
37+
38+
class KeySerializer;
39+
class Literal;
40+
41+
/// Shared file format helpers for bitmap global index.
42+
///
43+
/// Bitmap blocks for non-null keys are written as keys arrive. Dictionary blocks are flushed
44+
/// periodically, so these two kinds of blocks may be interleaved. The blocks at the end of the
45+
/// file have a fixed order:
46+
///
47+
/// +--------------------------------------------------+
48+
/// | Bitmap Block for key 1 (RoaringBitmap64) |
49+
/// +--------------------------------------------------+
50+
/// | ... Bitmap / Dictionary Blocks may interleave ...|
51+
/// +--------------------------------------------------+
52+
/// | Dictionary Block N |
53+
/// +--------------------------------------------------+
54+
/// | Null Rows Bitmap Block |
55+
/// +--------------------------------------------------+
56+
/// | Non-null Rows Bitmap Block |
57+
/// +--------------------------------------------------+
58+
/// | Dictionary Block Index |
59+
/// +--------------------------------------------------+
60+
/// | Footer (48 bytes) |
61+
/// +--------------------------------------------------+
62+
///
63+
/// Each dictionary entry maps one serialized key to its bitmap block:
64+
///
65+
/// Dictionary Block:
66+
/// [entry count]
67+
/// [key length | key bytes | bitmap offset | bitmap length] ...
68+
///
69+
/// The dictionary block index maps the first key of each dictionary block to that block:
70+
///
71+
/// Dictionary Block Index:
72+
/// [block count]
73+
/// [first key length | first key bytes | dictionary offset | dictionary length] ...
74+
///
75+
/// The footer stores the offset and length of the null rows bitmap, non-null rows bitmap and
76+
/// dictionary block index, followed by value count, version and magic. Dictionary blocks and the
77+
/// dictionary block index use the compressible-block encoding and are followed by a block trailer;
78+
/// bitmap blocks contain the serialized RoaringBitmap64 bytes directly.
79+
class BitmapGlobalIndexFormat {
80+
public:
81+
BitmapGlobalIndexFormat() = delete;
82+
~BitmapGlobalIndexFormat() = delete;
83+
84+
class BlockInfo;
85+
86+
/// Serialized bitmap dictionary key.
87+
class SerializedKey {
88+
public:
89+
explicit SerializedKey(std::shared_ptr<Bytes> bytes) : bytes_(std::move(bytes)) {}
90+
91+
static Result<SerializedKey> FromLiteral(const std::shared_ptr<KeySerializer>& serializer,
92+
const Literal& literal);
93+
94+
const std::shared_ptr<Bytes>& GetBytes() const {
95+
return bytes_;
96+
}
97+
98+
int32_t CompareTo(const SerializedKey& other) const;
99+
100+
bool operator==(const SerializedKey& other) const {
101+
return CompareTo(other) == 0;
102+
}
103+
104+
private:
105+
std::shared_ptr<Bytes> bytes_;
106+
};
107+
108+
/// Minimal random-access reader used by bitmap index format decoders.
109+
class SeekableReader {
110+
public:
111+
virtual ~SeekableReader() = default;
112+
113+
virtual Result<std::shared_ptr<Bytes>> Read(int64_t offset, int32_t length) = 0;
114+
115+
Result<std::shared_ptr<Bytes>> Read(const BlockInfo& block);
116+
};
117+
118+
/// Encoded block location within a bitmap index file.
119+
class BlockInfo {
120+
public:
121+
BlockInfo(int64_t offset, int32_t length) : offset_(offset), length_(length) {}
122+
123+
int64_t Offset() const {
124+
return offset_;
125+
}
126+
127+
int32_t Length() const {
128+
return length_;
129+
}
130+
131+
private:
132+
int64_t offset_;
133+
int32_t length_;
134+
};
135+
136+
/// Dictionary block metadata read from the block index.
137+
class DictionaryBlockMeta : public BlockInfo {
138+
public:
139+
DictionaryBlockMeta(SerializedKey first_key, int64_t offset, int32_t length)
140+
: BlockInfo(offset, length), first_key_(std::move(first_key)) {}
141+
142+
const SerializedKey& FirstKey() const {
143+
return first_key_;
144+
}
145+
146+
private:
147+
SerializedKey first_key_;
148+
};
149+
150+
/// One encoded dictionary key and its bitmap block.
151+
class DictionaryEntry {
152+
public:
153+
DictionaryEntry(SerializedKey key, BlockInfo bitmap_block)
154+
: key_(std::move(key)), bitmap_block_(std::move(bitmap_block)) {}
155+
156+
const SerializedKey& Key() const {
157+
return key_;
158+
}
159+
160+
const BlockInfo& BitmapBlock() const {
161+
return bitmap_block_;
162+
}
163+
164+
Result<int32_t> EstimatedSize() const;
165+
166+
private:
167+
SerializedKey key_;
168+
BlockInfo bitmap_block_;
169+
};
170+
171+
/// Decoded dictionary block.
172+
class DictionaryBlock {
173+
public:
174+
explicit DictionaryBlock(std::vector<DictionaryEntry> entries)
175+
: entries_(std::move(entries)) {}
176+
177+
const std::vector<DictionaryEntry>& Entries() const {
178+
return entries_;
179+
}
180+
181+
private:
182+
std::vector<DictionaryEntry> entries_;
183+
};
184+
185+
/// Bitmap index footer block references.
186+
class Footer {
187+
public:
188+
Footer(BlockInfo null_rows_block, BlockInfo non_null_rows_block, BlockInfo index_block)
189+
: null_rows_block_(std::move(null_rows_block)),
190+
non_null_rows_block_(std::move(non_null_rows_block)),
191+
index_block_(std::move(index_block)) {}
192+
193+
const BlockInfo& NullRowsBlock() const {
194+
return null_rows_block_;
195+
}
196+
197+
const BlockInfo& NonNullRowsBlock() const {
198+
return non_null_rows_block_;
199+
}
200+
201+
const BlockInfo& IndexBlock() const {
202+
return index_block_;
203+
}
204+
205+
private:
206+
BlockInfo null_rows_block_;
207+
BlockInfo non_null_rows_block_;
208+
BlockInfo index_block_;
209+
};
210+
211+
/// Streaming writer for encoded bitmap dictionary entries.
212+
class StreamingWriter {
213+
public:
214+
static Result<std::unique_ptr<StreamingWriter>> Create(
215+
const std::shared_ptr<OutputStream>& output_stream, int32_t dictionary_block_size,
216+
const std::shared_ptr<BlockCompressionFactory>& compression_factory,
217+
const std::shared_ptr<MemoryPool>& pool);
218+
219+
Status Write(SerializedKey key, const RoaringBitmap64& bitmap);
220+
221+
Status Finish(const RoaringBitmap64& null_rows, const RoaringBitmap64& non_null_rows);
222+
223+
private:
224+
StreamingWriter(const std::shared_ptr<OutputStream>& output_stream,
225+
int32_t dictionary_block_size,
226+
const std::shared_ptr<BlockCompressionFactory>& compression_factory,
227+
const std::shared_ptr<MemoryPool>& pool)
228+
: output_stream_(output_stream),
229+
dictionary_block_size_(dictionary_block_size),
230+
compression_factory_(compression_factory),
231+
pool_(pool) {}
232+
233+
Status FlushDictionaryBlock();
234+
Result<int32_t> EstimatedDictionaryBlockSizeAfter(const DictionaryEntry& entry) const;
235+
236+
std::shared_ptr<OutputStream> output_stream_;
237+
int32_t dictionary_block_size_;
238+
std::shared_ptr<BlockCompressionFactory> compression_factory_;
239+
std::shared_ptr<MemoryPool> pool_;
240+
std::vector<DictionaryBlockMeta> dictionary_block_metas_;
241+
std::vector<DictionaryEntry> current_dictionary_entries_;
242+
int32_t current_dictionary_entries_size_ = 0;
243+
int32_t value_count_ = 0;
244+
bool finished_ = false;
245+
};
246+
247+
static Result<Footer> ReadFooter(int64_t file_size, SeekableReader* reader);
248+
249+
static Result<std::vector<DictionaryBlockMeta>> ReadIndexBlock(const BlockInfo& index_block,
250+
SeekableReader* reader,
251+
MemoryPool* pool);
252+
253+
static Result<DictionaryBlock> ReadDictionaryBlock(const DictionaryBlockMeta& block,
254+
SeekableReader* reader, MemoryPool* pool);
255+
256+
static Result<RoaringBitmap64> ReadBitmap(const BlockInfo& block, SeekableReader* reader);
257+
258+
static constexpr int32_t kMagic = 0x42474958;
259+
static constexpr int32_t kVersion = 1;
260+
static constexpr int32_t kFooterLength = 48;
261+
262+
private:
263+
friend class StreamingWriter;
264+
265+
struct BlockEncoding {
266+
std::shared_ptr<Bytes> bytes;
267+
int32_t length;
268+
BlockCompressionType compression_type;
269+
};
270+
271+
static Status WriteFooter(const BlockInfo& null_rows_block,
272+
const BlockInfo& non_null_rows_block, const BlockInfo& index_block,
273+
int32_t value_count, OutputStream* output_stream);
274+
275+
static Result<BlockInfo> WriteBitmapBlock(const RoaringBitmap64& bitmap,
276+
OutputStream* output_stream, MemoryPool* pool);
277+
278+
static Result<DictionaryBlockMeta> WriteDictionaryBlock(
279+
const std::vector<DictionaryEntry>& entries, BlockCompressionFactory* compression_factory,
280+
OutputStream* output_stream, MemoryPool* pool);
281+
282+
static Result<BlockInfo> WriteIndexBlock(const std::vector<DictionaryBlockMeta>& blocks,
283+
BlockCompressionFactory* compression_factory,
284+
OutputStream* output_stream, MemoryPool* pool);
285+
286+
static Result<BlockInfo> WriteCompressibleBlock(const std::shared_ptr<Bytes>& uncompressed,
287+
BlockCompressionFactory* compression_factory,
288+
OutputStream* output_stream, MemoryPool* pool);
289+
290+
static Result<BlockEncoding> EncodeBlock(const std::shared_ptr<Bytes>& uncompressed,
291+
BlockCompressionFactory* compression_factory,
292+
MemoryPool* pool);
293+
294+
static Result<std::shared_ptr<Bytes>> ReadCompressibleBlock(const BlockInfo& block,
295+
SeekableReader* reader,
296+
MemoryPool* pool);
297+
298+
static Result<int32_t> EstimatedVarLenIntSize(int32_t value);
299+
static Result<int32_t> EstimatedVarLenLongSize(int64_t value);
300+
static Result<int32_t> EstimatedIndexBlockSize(const std::vector<DictionaryBlockMeta>& blocks);
301+
};
302+
303+
} // namespace paimon

0 commit comments

Comments
 (0)