Skip to content

Commit b5be8ad

Browse files
authored
Merge branch 'main' into blob-write-null-options
2 parents 723e248 + b1ffcb0 commit b5be8ad

22 files changed

Lines changed: 816 additions & 19 deletions

docs/source/user_guide.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ User Guide
3030
user_guide/data_types
3131
user_guide/primary_key_table
3232
user_guide/append_only_table
33+
user_guide/system_tables
3334
user_guide/write
3435
user_guide/commit
3536
user_guide/compaction
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.. Copyright 2026-present Alibaba Inc.
2+
3+
.. Licensed under the Apache License, Version 2.0 (the "License");
4+
.. you may not use this file except in compliance with the License.
5+
.. You may obtain a copy of the License at
6+
7+
.. http://www.apache.org/licenses/LICENSE-2.0
8+
9+
.. Unless required by applicable law or agreed to in writing, software
10+
.. distributed under the License is distributed on an "AS IS" BASIS,
11+
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
.. See the License for the specific language governing permissions and
13+
.. limitations under the License.
14+
15+
System Tables
16+
=============
17+
18+
Paimon C++ supports reading system tables by appending a system table suffix to
19+
the data table path. For example, ``/warehouse/db.db/orders$snapshots`` reads
20+
the snapshots system table for ``orders``.
21+
22+
Branch-qualified paths are also supported. For example,
23+
``/warehouse/db.db/orders$branch_audit$files`` reads the files system table from
24+
the ``audit`` branch.
25+
26+
Read-Optimized System Table
27+
---------------------------
28+
29+
The read-optimized system table is addressed by the ``$ro`` suffix:
30+
31+
.. code-block:: text
32+
33+
/warehouse/db.db/orders$ro
34+
35+
For primary-key tables, ``$ro`` only plans data files from the highest LSM
36+
level, which is the level produced by full compaction. This avoids merging data
37+
from multiple LSM levels during query planning and reading. The tradeoff is that
38+
the result may lag behind the latest committed data until a full compaction
39+
publishes the newest records into the highest level.
40+
41+
This stale view is not guaranteed to correspond to any single historical table
42+
snapshot. Full compaction may finish independently for different buckets. When
43+
``$ro`` scans the latest snapshot, the selected highest-level files can
44+
therefore combine bucket states produced by full-compaction commits at different
45+
snapshot IDs.
46+
47+
For primary-key tables, ``$ro`` also enables value-stats filtering, so file-level
48+
pruning of the reader predicate can be more aggressive than the base table.
49+
50+
For append-only tables, ``$ro`` has the same read behavior as the base table,
51+
including streaming reads.
52+
53+
Limitations
54+
~~~~~~~~~~~
55+
56+
- Primary-key ``$ro`` scans are batch-only. Streaming scans are not supported.
57+
- Freshness depends on full compaction frequency, and the result may not match
58+
any single historical snapshot across buckets.
59+
- Primary-key tables in bucket-unaware mode are not supported by the current C++
60+
scan path.
61+
62+
Typical Usage
63+
~~~~~~~~~~~~~
64+
65+
Use ``$ro`` for OLAP or batch workloads that can tolerate stale results and
66+
prefer reading compacted files directly. Use the base table path when the query
67+
must see the latest committed data.

include/paimon/defs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ struct PAIMON_EXPORT Options {
118118
/// "blob.target-file-size" - Target size of a blob file. Default is TARGET_FILE_SIZE.
119119
static const char BLOB_TARGET_FILE_SIZE[];
120120

121+
/// "blob.split-by-file-size" - Whether to consider blob file size as a factor when performing
122+
/// scan splitting. When unset, defaults to the negation of BLOB_AS_DESCRIPTOR.
123+
static const char BLOB_SPLIT_BY_FILE_SIZE[];
124+
121125
/// "partition.default-name" - The default partition name in case the dynamic partition column
122126
/// value is null/empty string. Default is "__DEFAULT_PARTITION__".
123127
static const char PARTITION_DEFAULT_NAME[];

src/paimon/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ set(PAIMON_CORE_SRCS
356356
core/table/system/binlog_system_table.cpp
357357
core/table/system/in_memory_system_table.cpp
358358
core/table/system/metadata_system_tables.cpp
359+
core/table/system/read_optimized_system_table.cpp
359360
core/table/system/system_table.cpp
360361
core/table/system/system_table_scan.cpp
361362
core/table/system/system_table_schema.cpp

src/paimon/common/defs.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const char Options::FILE_FORMAT[] = "file.format";
3333
const char Options::FILE_SYSTEM[] = "file-system";
3434
const char Options::TARGET_FILE_SIZE[] = "target-file-size";
3535
const char Options::BLOB_TARGET_FILE_SIZE[] = "blob.target-file-size";
36+
const char Options::BLOB_SPLIT_BY_FILE_SIZE[] = "blob.split-by-file-size";
3637
const char Options::PAGE_SIZE[] = "page-size";
3738
const char Options::PARTITION_DEFAULT_NAME[] = "partition.default-name";
3839
const char Options::FILE_COMPRESSION[] = "file.compression";

src/paimon/core/core_options.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,8 @@ struct CoreOptions::Impl {
448448
bool row_tracking_partition_group_on_commit = true;
449449
bool data_evolution_enabled = false;
450450
bool blob_view_resolve_enabled = true;
451+
bool blob_as_descriptor = false;
452+
std::optional<bool> blob_split_by_file_size;
451453
bool legacy_partition_name_enabled = true;
452454
bool global_index_enabled = true;
453455
std::optional<int32_t> global_index_thread_num;
@@ -578,6 +580,11 @@ struct CoreOptions::Impl {
578580
// Parse blob-view.resolve.enabled - whether to resolve blob view fields at read time
579581
PAIMON_RETURN_NOT_OK(
580582
parser.Parse<bool>(Options::BLOB_VIEW_RESOLVE_ENABLED, &blob_view_resolve_enabled));
583+
// Parse blob-as-descriptor - read blob field as descriptor rather than blob bytes
584+
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::BLOB_AS_DESCRIPTOR, &blob_as_descriptor));
585+
// Parse blob.split-by-file-size - whether blob file size counts in scan splitting
586+
PAIMON_RETURN_NOT_OK(
587+
parser.Parse(Options::BLOB_SPLIT_BY_FILE_SIZE, &blob_split_by_file_size));
581588
return Status::OK();
582589
}
583590

@@ -984,6 +991,10 @@ int64_t CoreOptions::GetBlobTargetFileSize() const {
984991
return impl_->blob_target_file_size.value();
985992
}
986993

994+
bool CoreOptions::BlobSplitByFileSize() const {
995+
return impl_->blob_split_by_file_size.value_or(!impl_->blob_as_descriptor);
996+
}
997+
987998
int64_t CoreOptions::GetCompactionFileSize(bool has_primary_key) const {
988999
// file size to join the compaction, we don't process on middle file size to avoid
9891000
// compact a same file twice (the compression is not calculate so accurately. the output

src/paimon/core/core_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class PAIMON_EXPORT CoreOptions {
7777
int64_t GetPageSize() const;
7878
int64_t GetTargetFileSize(bool has_primary_key) const;
7979
int64_t GetBlobTargetFileSize() const;
80+
bool BlobSplitByFileSize() const;
8081
int64_t GetCompactionFileSize(bool has_primary_key) const;
8182
std::string GetPartitionDefaultName() const;
8283

src/paimon/core/core_options_test.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ TEST(CoreOptionsTest, TestDefaultValue) {
4141
ASSERT_EQ(256 * 1024 * 1024L, core_options.GetTargetFileSize(/*has_primary_key=*/false));
4242
ASSERT_EQ(128 * 1024 * 1024L, core_options.GetTargetFileSize(/*has_primary_key=*/true));
4343
ASSERT_EQ(256 * 1024 * 1024L, core_options.GetBlobTargetFileSize());
44+
ASSERT_TRUE(core_options.BlobSplitByFileSize());
4445
ASSERT_EQ(187904815, core_options.GetCompactionFileSize(/*has_primary_key=*/false));
4546
ASSERT_EQ(93952404, core_options.GetCompactionFileSize(/*has_primary_key=*/true));
4647

@@ -240,6 +241,7 @@ TEST(CoreOptionsTest, TestFromMap) {
240241
{Options::DATA_EVOLUTION_ENABLED, "true"},
241242
{Options::BLOB_FIELD, "blob1,blob2"},
242243
{Options::BLOB_DESCRIPTOR_FIELD, "blob3,blob4"},
244+
{Options::BLOB_AS_DESCRIPTOR, "true"},
243245
{Options::BLOB_VIEW_FIELD, "blob5"},
244246
{Options::BLOB_VIEW_UPSTREAM_WAREHOUSE, "FILE:///tmp/blob_view_upstream_warehouse/"},
245247
{Options::BLOB_VIEW_RESOLVE_ENABLED, "false"},
@@ -385,6 +387,7 @@ TEST(CoreOptionsTest, TestFromMap) {
385387
ASSERT_TRUE(core_options.DataEvolutionEnabled());
386388
ASSERT_EQ(core_options.GetBlobFields(), std::vector<std::string>({"blob1", "blob2"}));
387389
ASSERT_EQ(core_options.GetBlobDescriptorFields(), std::vector<std::string>({"blob3", "blob4"}));
390+
ASSERT_FALSE(core_options.BlobSplitByFileSize());
388391
ASSERT_EQ(core_options.GetBlobViewFields(), std::vector<std::string>({"blob5"}));
389392
ASSERT_EQ(core_options.GetBlobInlineFields(),
390393
std::vector<std::string>({"blob3", "blob4", "blob5"}));
@@ -970,6 +973,12 @@ TEST(CoreOptionsTest, TestFallback) {
970973
ASSERT_EQ(options.GetBlobDescriptorFields(),
971974
std::vector<std::string>({"new_b1", "new_b2"}));
972975
}
976+
{
977+
ASSERT_OK_AND_ASSIGN(CoreOptions options,
978+
CoreOptions::FromMap({{Options::BLOB_AS_DESCRIPTOR, "true"},
979+
{Options::BLOB_SPLIT_BY_FILE_SIZE, "true"}}));
980+
ASSERT_TRUE(options.BlobSplitByFileSize());
981+
}
973982
}
974983

975984
TEST(CoreOptionsTest, TestMapStorageLayout) {

src/paimon/core/table/source/data_evolution_split_generator.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <functional>
2222
#include <iterator>
2323

24+
#include "paimon/common/data/blob_utils.h"
2425
#include "paimon/common/utils/bin_packing.h"
2526
#include "paimon/common/utils/range_helper.h"
2627
#include "paimon/core/io/data_file_meta.h"
@@ -40,11 +41,15 @@ Result<std::vector<SplitGenerator::SplitGroup>> DataEvolutionSplitGenerator::Spl
4041
PAIMON_ASSIGN_OR_RAISE(std::vector<std::vector<std::shared_ptr<DataFileMeta>>> ranges,
4142
range_helper.MergeOverlappingRanges(std::move(input)));
4243

43-
auto weight_func = [open_file_cost = open_file_cost_](
44+
auto weight_func = [open_file_cost = open_file_cost_, count_blob_size = count_blob_size_](
4445
const std::vector<std::shared_ptr<DataFileMeta>>& metas) -> int64_t {
4546
int64_t file_size_sum = 0;
4647
for (const auto& meta : metas) {
47-
file_size_sum += meta->file_size;
48+
if (BlobUtils::IsBlobFile(meta->file_name)) {
49+
file_size_sum += count_blob_size ? meta->file_size : open_file_cost;
50+
} else {
51+
file_size_sum += meta->file_size;
52+
}
4853
}
4954
return std::max(file_size_sum, open_file_cost);
5055
};

src/paimon/core/table/source/data_evolution_split_generator.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ struct DataFileMeta;
3030
/// Append data evolution table split generator, which implementation of `SplitGenerator`.
3131
class DataEvolutionSplitGenerator : public SplitGenerator {
3232
public:
33-
DataEvolutionSplitGenerator(int64_t target_split_size, int64_t open_file_cost)
34-
: target_split_size_(target_split_size), open_file_cost_(open_file_cost) {}
33+
DataEvolutionSplitGenerator(int64_t target_split_size, int64_t open_file_cost,
34+
bool count_blob_size)
35+
: target_split_size_(target_split_size),
36+
open_file_cost_(open_file_cost),
37+
count_blob_size_(count_blob_size) {}
3538

3639
Result<std::vector<SplitGroup>> SplitForBatch(
3740
std::vector<std::shared_ptr<DataFileMeta>>&& input) const override;
@@ -44,6 +47,7 @@ class DataEvolutionSplitGenerator : public SplitGenerator {
4447
private:
4548
int64_t target_split_size_;
4649
int64_t open_file_cost_;
50+
bool count_blob_size_;
4751
};
4852

4953
} // namespace paimon

0 commit comments

Comments
 (0)