blob: 4cb0c81aaa6ace25f7b34b7f074b39a6495617c4 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "paimon/core/core_options.h"
#include <cstring>
#include <limits>
#include <memory>
#include <optional>
#include <utility>
#include "fmt/format.h"
#include "paimon/common/fs/resolving_file_system.h"
#include "paimon/common/options/memory_size.h"
#include "paimon/common/options/time_duration.h"
#include "paimon/common/utils/path_util.h"
#include "paimon/common/utils/string_utils.h"
#include "paimon/core/options/expire_config.h"
#include "paimon/core/options/lookup_strategy.h"
#include "paimon/core/options/sort_order.h"
#include "paimon/core/utils/branch_manager.h"
#include "paimon/defs.h"
#include "paimon/format/file_format.h"
#include "paimon/format/file_format_factory.h"
#include "paimon/status.h"
namespace paimon {
// ConfigParser is a helper class for parsing configurations from a map of strings.
class ConfigParser {
public:
explicit ConfigParser(const std::map<std::string, std::string>& map) : config_map_(map) {}
// Parse basic type configurations
template <typename T>
Status Parse(const std::string& key, T* value) const {
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
auto result = StringUtils::StringToValue<T>(iter->second);
if (result) {
*value = result.value();
return Status::OK();
}
return Status::Invalid(fmt::format("Invalid Config [{}: {}]", key, iter->second));
}
return Status::OK(); // Return success even if the configuration does not exist
}
// Parse optional basic type configurations
template <typename T>
Status Parse(const std::string& key, std::optional<T>* value) const {
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
auto result = StringUtils::StringToValue<T>(iter->second);
if (result) {
*value = result.value();
return Status::OK();
}
return Status::Invalid(fmt::format("Invalid Config [{}: {}]", key, iter->second));
}
return Status::OK(); // Return success even if the configuration does not exist
}
// Parse list configurations
template <typename T>
Status ParseList(const std::string& key, const std::string& delimiter, std::vector<T>* list,
bool need_trim = false) const {
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
auto value_str_vec = StringUtils::Split(iter->second, delimiter, /*ignore_empty=*/true);
for (auto& value_str : value_str_vec) {
if (need_trim) {
StringUtils::Trim(&value_str);
}
if constexpr (std::is_same_v<T, std::string>) {
list->emplace_back(value_str);
} else {
auto value = StringUtils::StringToValue<T>(value_str);
if (!value) {
return Status::Invalid(
fmt::format("Invalid Config [{}: {}]", key, iter->second));
}
list->emplace_back(value.value());
}
}
}
return Status::OK(); // Return success even if the configuration does not exist
}
// Parse memory size configurations
template <typename T>
Status ParseMemorySize(const std::string& key, T* value) const {
static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, std::optional<int64_t>>,
"ParseMemorySize only supports int64_t and std::optional<int64_t>");
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
PAIMON_ASSIGN_OR_RAISE(*value, MemorySize::ParseBytes(iter->second));
}
return Status::OK();
}
// Parse time duration configurations
template <typename T>
Status ParseTimeDuration(const std::string& key, T* value) const {
static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, std::optional<int64_t>>,
"ParseTimeDuration only supports int64_t and std::optional<int64_t>");
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
PAIMON_ASSIGN_OR_RAISE(*value, TimeDuration::Parse(iter->second));
}
return Status::OK();
}
// Parse object configurations
template <typename Factory, typename ObjectType>
Status ParseObject(const std::string& key, const std::string& default_identifier,
std::shared_ptr<ObjectType>* value) const {
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
std::string normalized_value = StringUtils::ToLowerCase(iter->second);
PAIMON_ASSIGN_OR_RAISE(*value, Factory::Get(normalized_value, config_map_));
} else {
PAIMON_ASSIGN_OR_RAISE(
*value, Factory::Get(StringUtils::ToLowerCase(default_identifier), config_map_));
}
return Status::OK();
}
// Parse file system
Status ParseFileSystem(const std::map<std::string, std::string>& fs_scheme_to_identifier_map,
const std::shared_ptr<FileSystem>& specified_file_system,
std::shared_ptr<FileSystem>* value) const {
if (specified_file_system) {
// if exists user specified file system, first use
*value = specified_file_system;
return Status::OK();
}
std::string default_fs_identifier = "local";
auto iter = config_map_.find(Options::FILE_SYSTEM);
if (iter != config_map_.end()) {
default_fs_identifier = StringUtils::ToLowerCase(iter->second);
}
*value = std::make_shared<ResolvingFileSystem>(fs_scheme_to_identifier_map,
default_fs_identifier, config_map_);
return Status::OK();
}
// Parse SortOrder
Status ParseSortOrder(SortOrder* sort_order) const {
auto iter = config_map_.find(Options::SEQUENCE_FIELD_SORT_ORDER);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "ascending") {
*sort_order = SortOrder::ASCENDING;
} else if (str == "descending") {
*sort_order = SortOrder::DESCENDING;
} else {
return Status::Invalid(fmt::format("invalid sort order: {}", str));
}
}
return Status::OK();
}
// Parse LookupCompactMode
Status ParseLookupCompactMode(LookupCompactMode* mode) const {
auto iter = config_map_.find(Options::LOOKUP_COMPACT);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "radical") {
*mode = LookupCompactMode::RADICAL;
} else if (str == "gentle") {
*mode = LookupCompactMode::GENTLE;
} else {
return Status::Invalid(fmt::format("invalid lookup mode: {}", str));
}
}
return Status::OK();
}
// Parse SortEngine
Status ParseSortEngine(SortEngine* sort_engine) const {
auto iter = config_map_.find(Options::SORT_ENGINE);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "min-heap") {
*sort_engine = SortEngine::MIN_HEAP;
} else if (str == "loser-tree") {
*sort_engine = SortEngine::LOSER_TREE;
} else {
return Status::Invalid(fmt::format("invalid sort engine: {}", str));
}
}
return Status::OK();
}
// Parse MergeEngine
Status ParseMergeEngine(MergeEngine* merge_engine) const {
auto iter = config_map_.find(Options::MERGE_ENGINE);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "deduplicate") {
*merge_engine = MergeEngine::DEDUPLICATE;
} else if (str == "partial-update") {
*merge_engine = MergeEngine::PARTIAL_UPDATE;
} else if (str == "aggregation") {
*merge_engine = MergeEngine::AGGREGATE;
} else if (str == "first-row") {
*merge_engine = MergeEngine::FIRST_ROW;
} else {
return Status::Invalid(fmt::format("invalid merge engine: {}", str));
}
}
return Status::OK();
}
// Parse ChangelogProducer
Status ParseChangelogProducer(ChangelogProducer* changelog_producer) const {
auto iter = config_map_.find(Options::CHANGELOG_PRODUCER);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "none") {
*changelog_producer = ChangelogProducer::NONE;
} else if (str == "input") {
*changelog_producer = ChangelogProducer::INPUT;
} else if (str == "full-compaction") {
*changelog_producer = ChangelogProducer::FULL_COMPACTION;
} else if (str == "lookup") {
*changelog_producer = ChangelogProducer::LOOKUP;
} else {
return Status::Invalid(fmt::format("invalid changelog producer: {}", str));
}
}
return Status::OK();
}
// Parse ExternalPathStrategy
Status ParseExternalPathStrategy(ExternalPathStrategy* external_path_strategy) const {
auto iter = config_map_.find(Options::DATA_FILE_EXTERNAL_PATHS_STRATEGY);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "none") {
*external_path_strategy = ExternalPathStrategy::NONE;
} else if (str == "specific-fs") {
*external_path_strategy = ExternalPathStrategy::SPECIFIC_FS;
} else if (str == "round-robin") {
*external_path_strategy = ExternalPathStrategy::ROUND_ROBIN;
} else {
return Status::Invalid(fmt::format("invalid external path strategy: {}", str));
}
}
return Status::OK();
}
// Parse BucketFunctionType
Status ParseBucketFunctionType(BucketFunctionType* bucket_function_type) const {
auto iter = config_map_.find(Options::BUCKET_FUNCTION_TYPE);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
if (str == "default") {
*bucket_function_type = BucketFunctionType::DEFAULT;
} else if (str == "mod") {
*bucket_function_type = BucketFunctionType::MOD;
} else if (str == "hive") {
*bucket_function_type = BucketFunctionType::HIVE;
} else {
return Status::Invalid(fmt::format("invalid bucket function type: {}", str));
}
}
return Status::OK();
}
// Parse StartupMode
Status ParseStartupMode(StartupMode* startup_mode) const {
auto iter = config_map_.find(Options::SCAN_MODE);
if (iter != config_map_.end()) {
std::string str = StringUtils::ToLowerCase(iter->second);
PAIMON_ASSIGN_OR_RAISE(*startup_mode, StartupMode::FromString(str));
}
return Status::OK();
}
// parse file.format.per.level
Status ParseFileFormatPerLevel(
std::map<int32_t, std::shared_ptr<FileFormat>>* file_format_per_level_ptr) const {
auto& file_format_per_level = *file_format_per_level_ptr;
std::string file_format_per_level_str;
PAIMON_RETURN_NOT_OK(Parse(Options::FILE_FORMAT_PER_LEVEL, &file_format_per_level_str));
auto level2format =
StringUtils::Split(file_format_per_level_str, std::string(","), std::string(":"));
for (const auto& single_level : level2format) {
if (single_level.size() != 2) {
return Status::Invalid(
fmt::format("fail to parse key {}, value {} (usage example: 0:avro,3:parquet)",
Options::FILE_FORMAT_PER_LEVEL, file_format_per_level_str));
}
auto level = StringUtils::StringToValue<int32_t>(single_level[0]);
if (!level || level.value() < 0) {
return Status::Invalid(
fmt::format("fail to parse level {} from string to int in {}", single_level[0],
Options::FILE_FORMAT_PER_LEVEL));
}
std::shared_ptr<FileFormat> file_format;
PAIMON_RETURN_NOT_OK(ParseObject<FileFormatFactory>(
"_no_use", /*default_identifier=*/single_level[1], &file_format));
file_format_per_level[level.value()] = file_format;
}
return Status::OK();
}
// parse file.compression.per.level
Status ParseFileCompressionPerLevel(
std::map<int32_t, std::string>* file_compression_per_level_ptr) const {
auto& file_compression_per_level = *file_compression_per_level_ptr;
std::string file_compression_per_level_str;
PAIMON_RETURN_NOT_OK(
Parse(Options::FILE_COMPRESSION_PER_LEVEL, &file_compression_per_level_str));
auto level2compression =
StringUtils::Split(file_compression_per_level_str, std::string(","), std::string(":"));
for (const auto& single_level : level2compression) {
if (single_level.size() != 2) {
return Status::Invalid(fmt::format(
"fail to parse key {}, value {} (usage example: 0:lz4,1:zstd)",
Options::FILE_COMPRESSION_PER_LEVEL, file_compression_per_level_str));
}
auto level = StringUtils::StringToValue<int32_t>(single_level[0]);
if (!level || level.value() < 0) {
return Status::Invalid(
fmt::format("fail to parse level {} from string to int in {}", single_level[0],
Options::FILE_COMPRESSION_PER_LEVEL));
}
file_compression_per_level[level.value()] = single_level[1];
}
return Status::OK();
}
bool ContainsKey(const std::string& key) const {
return config_map_.find(key) != config_map_.end();
}
private:
const std::map<std::string, std::string> config_map_;
};
// Impl is a private implementation of CoreOptions,
// storing various configurable fields and their default values.
struct CoreOptions::Impl {
int64_t page_size = 64 * 1024;
std::optional<int64_t> target_file_size;
std::optional<int64_t> blob_target_file_size;
int64_t source_split_target_size = 128 * 1024 * 1024;
int64_t source_split_open_file_cost = 4 * 1024 * 1024;
int64_t manifest_target_file_size = 8 * 1024 * 1024;
int64_t deletion_vector_target_file_size = 2 * 1024 * 1024;
int64_t manifest_full_compaction_file_size = 16 * 1024 * 1024;
int64_t write_buffer_size = 256 * 1024 * 1024;
int64_t commit_timeout = std::numeric_limits<int64_t>::max();
std::shared_ptr<FileFormat> file_format;
std::shared_ptr<FileSystem> file_system;
std::shared_ptr<FileFormat> manifest_file_format;
std::optional<int64_t> scan_snapshot_id;
std::optional<int64_t> scan_timestamp_millis;
ExpireConfig expire_config;
std::vector<std::string> sequence_field;
std::vector<std::string> remove_record_on_sequence_group;
std::vector<std::string> blob_fields;
std::vector<std::string> blob_descriptor_fields;
std::vector<std::string> blob_view_fields;
std::vector<std::string> blob_external_storage_fields;
std::string partition_default_name = "__DEFAULT_PARTITION__";
StartupMode startup_mode = StartupMode::Default();
std::string file_compression = "zstd";
std::string manifest_compression = "zstd";
std::string branch = BranchManager::DEFAULT_MAIN_BRANCH;
std::string data_file_prefix = "data-";
std::string file_system_scheme_to_identifier_map_str;
std::optional<std::string> field_default_func;
std::optional<std::string> scan_fallback_branch;
std::optional<std::string> data_file_external_paths;
std::optional<std::string> blob_external_storage_path;
std::map<std::string, std::string> raw_options;
int32_t bucket = -1;
int32_t manifest_merge_min_count = 30;
int32_t read_batch_size = 1024;
int32_t write_batch_size = 1024;
int32_t local_sort_max_num_file_handles = 128;
int32_t commit_max_retries = 10;
int32_t compaction_min_file_num = 5;
int32_t compaction_max_size_amplification_percent = 200;
int32_t compaction_size_ratio = 1;
int32_t num_sorted_runs_compaction_trigger = 5;
std::optional<int32_t> num_sorted_runs_stop_trigger;
std::optional<int32_t> num_levels;
SortOrder sequence_field_sort_order = SortOrder::ASCENDING;
MergeEngine merge_engine = MergeEngine::DEDUPLICATE;
SortEngine sort_engine = SortEngine::LOSER_TREE;
ChangelogProducer changelog_producer = ChangelogProducer::NONE;
ExternalPathStrategy external_path_strategy = ExternalPathStrategy::NONE;
LookupCompactMode lookup_compact_mode = LookupCompactMode::RADICAL;
std::optional<int32_t> lookup_compact_max_interval;
BucketFunctionType bucket_function_type = BucketFunctionType::DEFAULT;
int32_t file_compression_zstd_level = 1;
int64_t write_buffer_spill_max_disk_size = std::numeric_limits<int64_t>::max();
bool ignore_delete = false;
bool write_buffer_spillable = true;
bool write_only = false;
bool deletion_vectors_enabled = false;
bool deletion_vectors_bitmap64 = false;
bool force_lookup = false;
bool lookup_wait = true;
bool partial_update_remove_record_on_delete = false;
bool aggregation_remove_record_on_delete = false;
bool table_read_sequence_number_enabled = false;
bool key_value_sequence_number_enabled = false;
bool file_index_read_enabled = true;
bool enable_adaptive_prefetch_strategy = true;
bool index_file_in_data_file_dir = false;
bool row_tracking_enabled = false;
bool row_tracking_partition_group_on_commit = true;
bool data_evolution_enabled = false;
bool legacy_partition_name_enabled = true;
bool global_index_enabled = true;
std::optional<int32_t> global_index_thread_num;
bool commit_force_compact = false;
bool compaction_force_rewrite_all_files = false;
bool compaction_force_up_level_0 = false;
std::optional<std::string> global_index_external_path;
std::optional<std::string> scan_tag_name;
std::optional<int64_t> optimized_compaction_interval;
std::optional<int64_t> compaction_total_size_threshold;
std::optional<int64_t> compaction_incremental_size_threshold;
int32_t compact_off_peak_start_hour = -1;
int32_t compact_off_peak_end_hour = -1;
int32_t compact_off_peak_ratio = 0;
bool lookup_cache_bloom_filter = true;
double lookup_cache_bloom_filter_fpp = 0.05;
bool lookup_remote_file_enabled = false;
int32_t lookup_remote_level_threshold = INT32_MIN;
CompressOptions lookup_compress_options{"zstd", 1};
CompressOptions spill_compress_options{"zstd", 1};
int64_t cache_page_size = 64 * 1024; // 64KB
std::map<int32_t, std::shared_ptr<FileFormat>> file_format_per_level;
std::map<int32_t, std::string> file_compression_per_level;
int64_t lookup_cache_max_memory = 256 * 1024 * 1024;
double lookup_cache_high_prio_pool_ratio = 0.25;
int64_t lookup_cache_file_retention_ms = 1 * 3600 * 1000; // 1 hour
int64_t lookup_cache_max_disk_size = INT64_MAX;
// Parse basic table options: bucket, partition, file sizes, batch sizes, file system, etc.
Status ParseBasicOptions(
const ConfigParser& parser, const std::shared_ptr<FileSystem>& specified_file_system,
const std::map<std::string, std::string>& fs_scheme_to_identifier_map) {
// Parse bucket - bucket number, -1 for dynamic bucket mode, >0 for fixed bucket mode
PAIMON_RETURN_NOT_OK(parser.Parse(Options::BUCKET, &bucket));
// Parse partition.default-name - default partition name for null/empty partition values
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::PARTITION_DEFAULT_NAME, &partition_default_name));
// Parse page-size - memory page size, default 64 kb
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::PAGE_SIZE, &page_size));
// Parse target-file-size - target size of a data file
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::TARGET_FILE_SIZE, &target_file_size));
// Parse blob.target-file-size - target size of a blob file
PAIMON_RETURN_NOT_OK(
parser.ParseMemorySize(Options::BLOB_TARGET_FILE_SIZE, &blob_target_file_size));
// Parse source.split.target-size - target size of a source split when scanning a bucket
PAIMON_RETURN_NOT_OK(
parser.ParseMemorySize(Options::SOURCE_SPLIT_TARGET_SIZE, &source_split_target_size));
// Parse source.split.open-file-cost - open file cost to avoid reading too many files
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::SOURCE_SPLIT_OPEN_FILE_COST,
&source_split_open_file_cost));
// Parse read.batch-size - read batch size for file formats
PAIMON_RETURN_NOT_OK(parser.Parse(Options::READ_BATCH_SIZE, &read_batch_size));
// Parse write.batch-size - write batch size for file formats
PAIMON_RETURN_NOT_OK(parser.Parse(Options::WRITE_BATCH_SIZE, &write_batch_size));
// Parse write-buffer-size - data to build up in memory before flushing to disk
PAIMON_RETURN_NOT_OK(
parser.ParseMemorySize(Options::WRITE_BUFFER_SIZE, &write_buffer_size));
// Parse write-buffer-spillable - whether write buffer may spill to disk
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::WRITE_BUFFER_SPILLABLE, &write_buffer_spillable));
// Parse write-buffer-spill.max-disk-size - max disk size for spill files
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::WRITE_BUFFER_SPILL_MAX_DISK_SIZE,
&write_buffer_spill_max_disk_size));
// Parse local-sort.max-num-file-handles - spill file handle cap for local merge
PAIMON_RETURN_NOT_OK(parser.Parse(Options::LOCAL_SORT_MAX_NUM_FILE_HANDLES,
&local_sort_max_num_file_handles));
// Parse spill-compression - compression codec for spill files
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::SPILL_COMPRESSION, &spill_compress_options.compress));
// Parse spill-compression.zstd-level - zstd level for spill compression, default 1
PAIMON_RETURN_NOT_OK(parser.Parse<int32_t>(Options::SPILL_COMPRESSION_ZSTD_LEVEL,
&(spill_compress_options.zstd_level)));
// Parse file-system - file system type, default "local"
PAIMON_RETURN_NOT_OK(parser.ParseFileSystem(fs_scheme_to_identifier_map,
specified_file_system, &file_system));
// Parse write-only - if true, compactions and snapshot expiration will be skipped
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::WRITE_ONLY, &write_only));
// Parse partition.legacy-name - use legacy ToString for partition names, default true
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::PARTITION_GENERATE_LEGACY_NAME,
&legacy_partition_name_enabled));
// Only for test, parse enable-adaptive-prefetch-strategy
PAIMON_RETURN_NOT_OK(parser.Parse<bool>("test.enable-adaptive-prefetch-strategy",
&enable_adaptive_prefetch_strategy));
// Parse data-file.external-paths - external paths for data files, comma separated
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::DATA_FILE_EXTERNAL_PATHS, &data_file_external_paths));
// Parse data-file.external-paths.strategy - strategy for selecting external path
PAIMON_RETURN_NOT_OK(parser.ParseExternalPathStrategy(&external_path_strategy));
// Parse data-file.prefix - file name prefix of data files, default "data-"
PAIMON_RETURN_NOT_OK(parser.Parse(Options::DATA_FILE_PREFIX, &data_file_prefix));
// Parse row-tracking.enabled - whether to enable unique row id for append table
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::ROW_TRACKING_ENABLED, &row_tracking_enabled));
// Parse row-tracking.partition-group-on-commit - whether to group delta files by partition
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::ROW_TRACKING_PARTITION_GROUP_ON_COMMIT,
&row_tracking_partition_group_on_commit));
// Parse data-evolution.enabled - whether to enable data evolution for row tracking
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::DATA_EVOLUTION_ENABLED, &data_evolution_enabled));
// Parse bucket-function - bucket function type, default "DEFAULT"
PAIMON_RETURN_NOT_OK(parser.ParseBucketFunctionType(&bucket_function_type));
// Parse blob-field - column names to store as blob type, comma separated
PAIMON_RETURN_NOT_OK(parser.ParseList<std::string>(
Options::BLOB_FIELD, Options::FIELDS_SEPARATOR, &blob_fields, /*need_trim=*/true));
// Parse blob-descriptor-field - BLOB fields stored inline as serialized descriptors
PAIMON_RETURN_NOT_OK(
parser.ParseList<std::string>(Options::BLOB_DESCRIPTOR_FIELD, Options::FIELDS_SEPARATOR,
&blob_descriptor_fields, /*need_trim=*/true));
if (blob_descriptor_fields.empty()) {
PAIMON_RETURN_NOT_OK(parser.ParseList<std::string>(
Options::FALLBACK_BLOB_DESCRIPTOR_FIELD, Options::FIELDS_SEPARATOR,
&blob_descriptor_fields, /*need_trim=*/true));
}
// Parse blob-view-field - BLOB fields stored inline as serialized view metadata
PAIMON_RETURN_NOT_OK(parser.ParseList<std::string>(Options::BLOB_VIEW_FIELD,
Options::FIELDS_SEPARATOR,
&blob_view_fields, /*need_trim=*/true));
// Parse blob-external-storage-field - descriptor BLOB fields written to external storage
PAIMON_RETURN_NOT_OK(parser.ParseList<std::string>(
Options::BLOB_EXTERNAL_STORAGE_FIELD, Options::FIELDS_SEPARATOR,
&blob_external_storage_fields, /*need_trim=*/true));
// Parse blob-external-storage-path - external storage path for configured BLOB fields
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::BLOB_EXTERNAL_STORAGE_PATH, &blob_external_storage_path));
return Status::OK();
}
// Parse data file format, compression, and per-level format/compression configurations.
Status ParseFileFormatOptions(const ConfigParser& parser) {
// Parse file.format - data file format, default "parquet"
PAIMON_RETURN_NOT_OK(parser.ParseObject<FileFormatFactory>(
Options::FILE_FORMAT, /*default_identifier=*/"parquet", &file_format));
// Parse file.compression - default file compression, default "zstd"
PAIMON_RETURN_NOT_OK(parser.Parse(Options::FILE_COMPRESSION, &file_compression));
// Parse file.compression.zstd-level - zstd compression level, default 1
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::FILE_COMPRESSION_ZSTD_LEVEL, &file_compression_zstd_level));
// Parse file.format.per.level - different file format for different levels
PAIMON_RETURN_NOT_OK(parser.ParseFileFormatPerLevel(&file_format_per_level));
// Parse file.compression.per.level - different compression for different levels
PAIMON_RETURN_NOT_OK(parser.ParseFileCompressionPerLevel(&file_compression_per_level));
return Status::OK();
}
// Parse manifest file configurations: format, compression, merge, and compaction thresholds.
Status ParseManifestOptions(const ConfigParser& parser) {
// Parse manifest.format - manifest file format, default "avro"
PAIMON_RETURN_NOT_OK(parser.ParseObject<FileFormatFactory>(
Options::MANIFEST_FORMAT, /*default_identifier=*/"avro", &manifest_file_format));
// Parse manifest.compression - manifest file compression, default "zstd"
PAIMON_RETURN_NOT_OK(parser.Parse(Options::MANIFEST_COMPRESSION, &manifest_compression));
// Parse manifest.target-file-size - suggested manifest file size, default 8MB
PAIMON_RETURN_NOT_OK(
parser.ParseMemorySize(Options::MANIFEST_TARGET_FILE_SIZE, &manifest_target_file_size));
// Parse manifest.merge-min-count - minimum ManifestFileMeta count to trigger merge
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::MANIFEST_MERGE_MIN_COUNT, &manifest_merge_min_count));
// Parse manifest.full-compaction-threshold-size - size threshold for full compaction
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::MANIFEST_FULL_COMPACTION_FILE_SIZE,
&manifest_full_compaction_file_size));
return Status::OK();
}
// Parse snapshot expiration and retention configurations.
Status ParseExpireOptions(const ConfigParser& parser) {
// Parse snapshot.num-retained.min - minimum completed snapshots to retain, default 10
int32_t snapshot_num_retain_min = 10;
// Parse snapshot.num-retained.max - maximum completed snapshots to retain
int32_t snapshot_num_retain_max = std::numeric_limits<int32_t>::max();
// Parse snapshot.expire.limit - maximum snapshots allowed to expire at a time, default 50
int32_t snapshot_expire_limit = 50;
// Parse snapshot.time-retained - maximum time of completed snapshots to retain
int64_t snapshot_time_retained = 1 * 3600 * 1000; // 1 hour
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::SNAPSHOT_NUM_RETAINED_MIN, &snapshot_num_retain_min));
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::SNAPSHOT_NUM_RETAINED_MAX, &snapshot_num_retain_max));
PAIMON_RETURN_NOT_OK(parser.Parse(Options::SNAPSHOT_EXPIRE_LIMIT, &snapshot_expire_limit));
PAIMON_RETURN_NOT_OK(
parser.ParseTimeDuration(Options::SNAPSHOT_TIME_RETAINED, &snapshot_time_retained));
// Parse snapshot.clean-empty-directories - whether to clean empty dirs on expiration
bool snapshot_clean_empty_directories = false;
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::SNAPSHOT_CLEAN_EMPTY_DIRECTORIES,
&snapshot_clean_empty_directories));
expire_config =
ExpireConfig(snapshot_num_retain_max, snapshot_num_retain_min, snapshot_time_retained,
snapshot_expire_limit, snapshot_clean_empty_directories);
return Status::OK();
}
// Parse commit configurations: timeout, retries, and force-compact.
Status ParseCommitOptions(const ConfigParser& parser) {
// Parse commit.force-compact - whether to force compaction before commit, default false
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::COMMIT_FORCE_COMPACT, &commit_force_compact));
// Parse commit.timeout - timeout duration of retry when commit failed
PAIMON_RETURN_NOT_OK(parser.ParseTimeDuration(Options::COMMIT_TIMEOUT, &commit_timeout));
// Parse commit.max-retries - maximum retries when commit failed, default 10
PAIMON_RETURN_NOT_OK(parser.Parse(Options::COMMIT_MAX_RETRIES, &commit_max_retries));
return Status::OK();
}
// Parse merge engine, sort engine, sequence field, changelog, and partial-update options.
Status ParseMergeAndSequenceOptions(const ConfigParser& parser) {
// Parse sequence.field - field that generates sequence number for primary key table
PAIMON_RETURN_NOT_OK(parser.ParseList<std::string>(
Options::SEQUENCE_FIELD, Options::FIELDS_SEPARATOR, &sequence_field));
// Parse sequence.field.sort-order - order of sequence field, default "ascending"
PAIMON_RETURN_NOT_OK(parser.ParseSortOrder(&sequence_field_sort_order));
// Parse sort-engine - sort engine for primary key table, default "loser-tree"
PAIMON_RETURN_NOT_OK(parser.ParseSortEngine(&sort_engine));
// Parse merge-engine - merge engine for primary key table, default "deduplicate"
PAIMON_RETURN_NOT_OK(parser.ParseMergeEngine(&merge_engine));
// Parse ignore-delete - whether to ignore delete records, default false
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::IGNORE_DELETE, &ignore_delete));
// Parse fields.default-aggregate-function - default agg function for partial-update
PAIMON_RETURN_NOT_OK(parser.Parse(Options::FIELDS_DEFAULT_AGG_FUNC, &field_default_func));
// Parse changelog-producer - whether to double write to a changelog file, default "none"
PAIMON_RETURN_NOT_OK(parser.ParseChangelogProducer(&changelog_producer));
// Parse partial-update.remove-record-on-delete - remove whole row on delete
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::PARTIAL_UPDATE_REMOVE_RECORD_ON_DELETE,
&partial_update_remove_record_on_delete));
// Parse aggregation_remove_record_on_delete
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::AGGREGATION_REMOVE_RECORD_ON_DELETE,
&aggregation_remove_record_on_delete));
// Parse table-read.sequence-number.enabled - expose sequence number in system tables
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::TABLE_READ_SEQUENCE_NUMBER_ENABLED,
&table_read_sequence_number_enabled));
// Parse key-value.sequence_number.enabled - internal sequence number read switch
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::KEY_VALUE_SEQUENCE_NUMBER_ENABLED,
&key_value_sequence_number_enabled));
// Parse partial-update.remove-record-on-sequence-group
PAIMON_RETURN_NOT_OK(parser.ParseList<std::string>(
Options::PARTIAL_UPDATE_REMOVE_RECORD_ON_SEQUENCE_GROUP, Options::FIELDS_SEPARATOR,
&remove_record_on_sequence_group));
return Status::OK();
}
// Parse deletion vector configurations.
Status ParseDeletionVectorOptions(const ConfigParser& parser) {
// Parse deletion-vectors.enabled - whether to enable deletion vectors mode, default false
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::DELETION_VECTORS_ENABLED, &deletion_vectors_enabled));
// Parse deletion-vector.index-file.target-size - target size of dv index file, default 2MB
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::DELETION_VECTOR_INDEX_FILE_TARGET_SIZE,
&deletion_vector_target_file_size));
// Parse deletion-vectors.bitmap64 - enable 64 bit bitmap implementation, default false
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::DELETION_VECTOR_BITMAP64, &deletion_vectors_bitmap64));
return Status::OK();
}
// Parse scan, branch, and tag related configurations.
Status ParseScanAndBranchOptions(const ConfigParser& parser) {
// Parse scan.snapshot-id - optional snapshot id for "from-snapshot" scan mode
PAIMON_RETURN_NOT_OK(parser.Parse(Options::SCAN_SNAPSHOT_ID, &scan_snapshot_id));
// Parse scan.timestamp-millis and scan.timestamp
std::string scan_timestamp_str;
PAIMON_RETURN_NOT_OK(parser.Parse(Options::SCAN_TIMESTAMP, &scan_timestamp_str));
PAIMON_RETURN_NOT_OK(parser.Parse(Options::SCAN_TIMESTAMP_MILLIS, &scan_timestamp_millis));
if (scan_timestamp_millis != std::nullopt && !scan_timestamp_str.empty()) {
return Status::Invalid(
"scan.timestamp-millis and scan.timestamp cannot be set at the same time");
}
if (!scan_timestamp_str.empty()) {
PAIMON_ASSIGN_OR_RAISE(int64_t millis,
StringUtils::StringToTimestampMillis(scan_timestamp_str));
scan_timestamp_millis = millis;
}
// Parse scan.mode - scanning behavior of the source, default "default"
PAIMON_RETURN_NOT_OK(parser.ParseStartupMode(&startup_mode));
// Parse scan.fallback-branch - fallback branch when partition not found
PAIMON_RETURN_NOT_OK(parser.Parse(Options::SCAN_FALLBACK_BRANCH, &scan_fallback_branch));
// Parse branch - branch name, default "main"
PAIMON_RETURN_NOT_OK(parser.Parse(Options::BRANCH, &branch));
// Parse scan.tag-name - optional tag name for "from-snapshot" scan mode
PAIMON_RETURN_NOT_OK(parser.Parse(Options::SCAN_TAG_NAME, &scan_tag_name));
return Status::OK();
}
// Parse index-related configurations: file index, global index.
Status ParseIndexOptions(const ConfigParser& parser) {
// Parse file-index.read.enabled - whether to enable reading file index, default true
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::FILE_INDEX_READ_ENABLED, &file_index_read_enabled));
// Parse index-file-in-data-file-dir - whether index file in data file directory
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::INDEX_FILE_IN_DATA_FILE_DIR, &index_file_in_data_file_dir));
// Parse global-index.enabled - whether to enable global index for scan, default true
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::GLOBAL_INDEX_ENABLED, &global_index_enabled));
// Parse global-index.thread-num - the maximum number of concurrent scanner for global
// index, no default value
PAIMON_RETURN_NOT_OK(
parser.Parse<int32_t>(Options::GLOBAL_INDEX_THREAD_NUM, &global_index_thread_num));
// Parse global-index.external-path - global index root directory
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::GLOBAL_INDEX_EXTERNAL_PATH, &global_index_external_path));
return Status::OK();
}
// Parse compaction configurations: sorted run triggers, size ratios, thresholds, off-peak.
Status ParseCompactionOptions(const ConfigParser& parser) {
// Parse compaction.min.file-num - minimum file number to trigger compaction, default 5
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::COMPACTION_MIN_FILE_NUM, &compaction_min_file_num));
// Parse compaction.max-size-amplification-percent - size amplification percent, default 200
PAIMON_RETURN_NOT_OK(
parser.Parse<int32_t>(Options::COMPACTION_MAX_SIZE_AMPLIFICATION_PERCENT,
&compaction_max_size_amplification_percent));
// Parse compaction.size-ratio - percentage flexibility for sorted run comparison, default 1
PAIMON_RETURN_NOT_OK(
parser.Parse<int32_t>(Options::COMPACTION_SIZE_RATIO, &compaction_size_ratio));
// Parse num-sorted-run.compaction-trigger - sorted run number to trigger compaction
PAIMON_RETURN_NOT_OK(parser.Parse<int32_t>(Options::NUM_SORTED_RUNS_COMPACTION_TRIGGER,
&num_sorted_runs_compaction_trigger));
// Parse num-sorted-run.stop-trigger - sorted run number to stop writes
PAIMON_RETURN_NOT_OK(parser.Parse<int32_t>(Options::NUM_SORTED_RUNS_STOP_TRIGGER,
&num_sorted_runs_stop_trigger));
// Parse num-levels - total level number for LSM tree
PAIMON_RETURN_NOT_OK(parser.Parse<int32_t>(Options::NUM_LEVELS, &num_levels));
// Parse compaction.force-rewrite-all-files - force pick all files for full compaction
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::COMPACTION_FORCE_REWRITE_ALL_FILES,
&compaction_force_rewrite_all_files));
// Parse compaction.force-up-level-0 - always include all level 0 files in candidates
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::COMPACTION_FORCE_UP_LEVEL_0, &compaction_force_up_level_0));
// Parse compaction.optimization-interval - how often to perform optimization compaction
PAIMON_RETURN_NOT_OK(parser.ParseTimeDuration(Options::COMPACTION_OPTIMIZATION_INTERVAL,
&optimized_compaction_interval));
// Parse compaction.total-size-threshold - force full compaction when total size is smaller
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::COMPACTION_TOTAL_SIZE_THRESHOLD,
&compaction_total_size_threshold));
// Parse compaction.incremental-size-threshold - force full compaction when incremental size
// is bigger
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::COMPACTION_INCREMENTAL_SIZE_THRESHOLD,
&compaction_incremental_size_threshold));
// Parse compaction.offpeak.start.hour - start of off-peak hours (0-23), -1 to disable
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::COMPACT_OFFPEAK_START_HOUR, &compact_off_peak_start_hour));
// Parse compaction.offpeak.end.hour - end of off-peak hours (0-23), -1 to disable
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::COMPACT_OFFPEAK_END_HOUR, &compact_off_peak_end_hour));
// Parse compaction.offpeak-ratio - more aggressive ratio during off-peak hours, default 0
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::COMPACTION_OFFPEAK_RATIO, &compact_off_peak_ratio));
return Status::OK();
}
// Parse lookup configurations: compact mode, bloom filter, remote file, cache, compression.
Status ParseLookupOptions(const ConfigParser& parser) {
// Parse force-lookup - whether to force lookup for compaction, default false
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::FORCE_LOOKUP, &force_lookup));
// Parse lookup-wait - commit will wait for compaction by lookup, default true
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::LOOKUP_WAIT, &lookup_wait));
// Parse lookup-compact - lookup compact mode, default RADICAL
PAIMON_RETURN_NOT_OK(parser.ParseLookupCompactMode(&lookup_compact_mode));
// Parse lookup-compact.max-interval - max interval for gentle mode lookup compaction
PAIMON_RETURN_NOT_OK(
parser.Parse(Options::LOOKUP_COMPACT_MAX_INTERVAL, &lookup_compact_max_interval));
// Parse lookup.cache.bloom.filter.enabled - enable bloom filter for lookup cache
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(Options::LOOKUP_CACHE_BLOOM_FILTER_ENABLED,
&lookup_cache_bloom_filter));
// Parse lookup.cache.bloom.filter.fpp - false positive probability, default 0.05
PAIMON_RETURN_NOT_OK(parser.Parse<double>(Options::LOOKUP_CACHE_BLOOM_FILTER_FPP,
&lookup_cache_bloom_filter_fpp));
// Parse lookup.remote-file.enabled - whether to enable remote file for lookup
PAIMON_RETURN_NOT_OK(
parser.Parse<bool>(Options::LOOKUP_REMOTE_FILE_ENABLED, &lookup_remote_file_enabled));
// Parse lookup.remote-file.level-threshold - level threshold for remote lookup files
PAIMON_RETURN_NOT_OK(parser.Parse<int32_t>(Options::LOOKUP_REMOTE_LEVEL_THRESHOLD,
&lookup_remote_level_threshold));
// Parse lookup.cache-spill-compression - spill compression for lookup cache, default "zstd"
PAIMON_RETURN_NOT_OK(parser.Parse(Options::LOOKUP_CACHE_SPILL_COMPRESSION,
&lookup_compress_options.compress));
// Parse spill-compression.zstd-level - zstd level for spill compression, default 1
PAIMON_RETURN_NOT_OK(parser.Parse<int32_t>(Options::SPILL_COMPRESSION_ZSTD_LEVEL,
&(lookup_compress_options.zstd_level)));
// Parse cache-page-size - memory page size for caching, default 64 kb
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::CACHE_PAGE_SIZE, &cache_page_size));
// Parse lookup.cache-max-memory-size - max memory size for lookup cache, default 256 mb
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::LOOKUP_CACHE_MAX_MEMORY_SIZE,
&lookup_cache_max_memory));
// Parse lookup.cache.high-priority-pool-ratio - fraction for high-priority data, default
// 0.25
PAIMON_RETURN_NOT_OK(parser.Parse<double>(Options::LOOKUP_CACHE_HIGH_PRIO_POOL_RATIO,
&lookup_cache_high_prio_pool_ratio));
if (lookup_cache_high_prio_pool_ratio < 0.0 || lookup_cache_high_prio_pool_ratio >= 1.0) {
return Status::Invalid(fmt::format(
"The high priority pool ratio should in the range [0, 1), while input is {}",
lookup_cache_high_prio_pool_ratio));
}
// Parse lookup.cache-file-retention - cached files retention time, default "1 hour"
PAIMON_RETURN_NOT_OK(parser.ParseTimeDuration(Options::LOOKUP_CACHE_FILE_RETENTION,
&lookup_cache_file_retention_ms));
// Parse lookup.cache-max-disk-size - max disk size for lookup cache, default unlimited
PAIMON_RETURN_NOT_OK(parser.ParseMemorySize(Options::LOOKUP_CACHE_MAX_DISK_SIZE,
&lookup_cache_max_disk_size));
return Status::OK();
}
};
// Parse configurations from a map and return a populated CoreOptions object.
Result<CoreOptions> CoreOptions::FromMap(
const std::map<std::string, std::string>& options_map,
const std::shared_ptr<FileSystem>& specified_file_system,
const std::map<std::string, std::string>& fs_scheme_to_identifier_map) {
CoreOptions options;
auto& impl = options.impl_;
impl->raw_options = options_map;
ConfigParser parser(options_map);
PAIMON_RETURN_NOT_OK(
impl->ParseBasicOptions(parser, specified_file_system, fs_scheme_to_identifier_map));
PAIMON_RETURN_NOT_OK(impl->ParseFileFormatOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseManifestOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseExpireOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseCommitOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseMergeAndSequenceOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseDeletionVectorOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseScanAndBranchOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseIndexOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseCompactionOptions(parser));
PAIMON_RETURN_NOT_OK(impl->ParseLookupOptions(parser));
return options;
}
CoreOptions::CoreOptions() : impl_(std::make_unique<Impl>()) {}
CoreOptions::CoreOptions(const CoreOptions& rhs)
: impl_(std::make_unique<Impl>(*(rhs.impl_.get()))) {}
CoreOptions& CoreOptions::operator=(const CoreOptions& rhs) {
if (this != &rhs) {
impl_ = std::make_unique<Impl>(*(rhs.impl_.get()));
}
return *this;
}
CoreOptions::~CoreOptions() = default;
int32_t CoreOptions::GetBucket() const {
return impl_->bucket;
}
std::shared_ptr<FileFormat> CoreOptions::GetWriteFileFormat(int32_t level) const {
auto iter = impl_->file_format_per_level.find(level);
if (iter != impl_->file_format_per_level.end()) {
return iter->second;
}
return impl_->file_format;
}
std::shared_ptr<FileFormat> CoreOptions::GetFileFormat() const {
return impl_->file_format;
}
std::shared_ptr<FileSystem> CoreOptions::GetFileSystem() const {
return impl_->file_system;
}
const std::string& CoreOptions::GetFileCompression() const {
return impl_->file_compression;
}
const std::string& CoreOptions::GetWriteFileCompression(int32_t level) const {
auto iter = impl_->file_compression_per_level.find(level);
if (iter != impl_->file_compression_per_level.end()) {
return iter->second;
}
return impl_->file_compression;
}
int32_t CoreOptions::GetFileCompressionZstdLevel() const {
return impl_->file_compression_zstd_level;
}
int64_t CoreOptions::GetPageSize() const {
return impl_->page_size;
}
int64_t CoreOptions::GetTargetFileSize(bool has_primary_key) const {
if (impl_->target_file_size == std::nullopt) {
return has_primary_key ? 128 * 1024 * 1024 : 256 * 1024 * 1024;
}
return impl_->target_file_size.value();
}
int64_t CoreOptions::GetBlobTargetFileSize() const {
if (impl_->blob_target_file_size == std::nullopt) {
return GetTargetFileSize(/*has_primary_key=*/false);
}
return impl_->blob_target_file_size.value();
}
int64_t CoreOptions::GetCompactionFileSize(bool has_primary_key) const {
// file size to join the compaction, we don't process on middle file size to avoid
// compact a same file twice (the compression is not calculate so accurately. the output
// file maybe be less than target file generated by rolling file write).
return GetTargetFileSize(has_primary_key) / 10 * 7;
}
std::string CoreOptions::GetPartitionDefaultName() const {
return impl_->partition_default_name;
}
std::shared_ptr<FileFormat> CoreOptions::GetManifestFormat() const {
return impl_->manifest_file_format;
}
int64_t CoreOptions::GetSourceSplitTargetSize() const {
return impl_->source_split_target_size;
}
int64_t CoreOptions::GetSourceSplitOpenFileCost() const {
return impl_->source_split_open_file_cost;
}
std::optional<int64_t> CoreOptions::GetScanSnapshotId() const {
return impl_->scan_snapshot_id;
}
std::optional<int64_t> CoreOptions::GetScanTimestampMillis() const {
return impl_->scan_timestamp_millis;
}
int64_t CoreOptions::GetManifestTargetFileSize() const {
return impl_->manifest_target_file_size;
}
int32_t CoreOptions::GetManifestMergeMinCount() const {
return impl_->manifest_merge_min_count;
}
int64_t CoreOptions::GetManifestFullCompactionThresholdSize() const {
return impl_->manifest_full_compaction_file_size;
}
const std::string& CoreOptions::GetManifestCompression() const {
return impl_->manifest_compression;
}
StartupMode CoreOptions::GetStartupMode() const {
if (impl_->startup_mode == StartupMode::Default()) {
if (GetScanSnapshotId() != std::nullopt || GetScanTagName() != std::nullopt) {
return StartupMode::FromSnapshot();
}
if (GetScanTimestampMillis() != std::nullopt) {
return StartupMode::FromTimestamp();
}
return StartupMode::LatestFull();
}
return impl_->startup_mode;
}
int32_t CoreOptions::GetReadBatchSize() const {
return impl_->read_batch_size;
}
int32_t CoreOptions::GetWriteBatchSize() const {
return impl_->write_batch_size;
}
int64_t CoreOptions::GetWriteBufferSize() const {
return impl_->write_buffer_size;
}
bool CoreOptions::GetWriteBufferSpillable() const {
return impl_->write_buffer_spillable;
}
int64_t CoreOptions::GetWriteBufferSpillMaxDiskSize() const {
return impl_->write_buffer_spill_max_disk_size;
}
int32_t CoreOptions::GetLocalSortMaxNumFileHandles() const {
return impl_->local_sort_max_num_file_handles;
}
const CompressOptions& CoreOptions::GetSpillCompressOptions() const {
return impl_->spill_compress_options;
}
bool CoreOptions::CommitForceCompact() const {
return impl_->commit_force_compact;
}
int64_t CoreOptions::GetCommitTimeout() const {
return impl_->commit_timeout;
}
int32_t CoreOptions::GetCommitMaxRetries() const {
return impl_->commit_max_retries;
}
int32_t CoreOptions::GetCompactionMinFileNum() const {
return impl_->compaction_min_file_num;
}
int32_t CoreOptions::GetCompactionMaxSizeAmplificationPercent() const {
return impl_->compaction_max_size_amplification_percent;
}
int32_t CoreOptions::GetCompactionSizeRatio() const {
return impl_->compaction_size_ratio;
}
int32_t CoreOptions::GetNumSortedRunsCompactionTrigger() const {
return impl_->num_sorted_runs_compaction_trigger;
}
int32_t CoreOptions::GetNumSortedRunsStopTrigger() const {
int32_t compact_trigger = GetNumSortedRunsCompactionTrigger();
int32_t stop_trigger = 0;
if (impl_->num_sorted_runs_stop_trigger.has_value()) {
stop_trigger = impl_->num_sorted_runs_stop_trigger.value();
} else {
int64_t computed = static_cast<int64_t>(compact_trigger) + 3;
if (computed > std::numeric_limits<int32_t>::max()) {
computed = std::numeric_limits<int32_t>::max();
}
stop_trigger = static_cast<int32_t>(computed);
}
return std::max(compact_trigger, stop_trigger);
}
int32_t CoreOptions::GetNumLevels() const {
// By default, this ensures that the compaction does not fall to level 0, but at least to
// level 1
if (impl_->num_levels.has_value()) {
return impl_->num_levels.value();
}
int64_t incremented = static_cast<int64_t>(GetNumSortedRunsCompactionTrigger()) + 1;
if (incremented > std::numeric_limits<int32_t>::max()) {
incremented = std::numeric_limits<int32_t>::max();
}
return static_cast<int32_t>(incremented);
}
LookupCompactMode CoreOptions::GetLookupCompactMode() const {
return impl_->lookup_compact_mode;
}
int32_t CoreOptions::GetLookupCompactMaxInterval() const {
int32_t compact_trigger = GetNumSortedRunsCompactionTrigger();
int32_t max_interval;
if (impl_->lookup_compact_max_interval.has_value()) {
max_interval = impl_->lookup_compact_max_interval.value();
} else {
int64_t doubled = static_cast<int64_t>(compact_trigger) * 2;
if (doubled > std::numeric_limits<int32_t>::max()) {
doubled = std::numeric_limits<int32_t>::max();
}
max_interval = static_cast<int32_t>(doubled);
}
if (max_interval < compact_trigger) {
max_interval = compact_trigger;
}
return max_interval;
}
const ExpireConfig& CoreOptions::GetExpireConfig() const {
return impl_->expire_config;
}
const std::vector<std::string>& CoreOptions::GetSequenceField() const {
return impl_->sequence_field;
}
bool CoreOptions::SequenceFieldSortOrderIsAscending() const {
return impl_->sequence_field_sort_order == SortOrder::ASCENDING;
}
MergeEngine CoreOptions::GetMergeEngine() const {
return impl_->merge_engine;
}
SortEngine CoreOptions::GetSortEngine() const {
return impl_->sort_engine;
}
bool CoreOptions::IgnoreDelete() const {
return impl_->ignore_delete;
}
bool CoreOptions::WriteOnly() const {
return impl_->write_only;
}
std::optional<std::string> CoreOptions::GetFieldsDefaultFunc() const {
return impl_->field_default_func;
}
bool CoreOptions::EnableAdaptivePrefetchStrategy() const {
return impl_->enable_adaptive_prefetch_strategy;
}
Result<std::optional<std::string>> CoreOptions::GetFieldAggFunc(
const std::string& field_name) const {
ConfigParser parser(impl_->raw_options);
std::optional<std::string> field_agg_func;
std::string key = std::string(Options::FIELDS_PREFIX) + "." + field_name + "." +
std::string(Options::AGG_FUNCTION);
PAIMON_RETURN_NOT_OK(parser.Parse(key, &field_agg_func));
return field_agg_func;
}
Result<bool> CoreOptions::FieldAggIgnoreRetract(const std::string& field_name) const {
ConfigParser parser(impl_->raw_options);
bool field_agg_ignore_retract = false;
std::string key = std::string(Options::FIELDS_PREFIX) + "." + field_name + "." +
std::string(Options::IGNORE_RETRACT);
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(key, &field_agg_ignore_retract));
return field_agg_ignore_retract;
}
Result<std::string> CoreOptions::FieldListAggDelimiter(const std::string& field_name) const {
ConfigParser parser(impl_->raw_options);
std::string delimiter = ",";
std::string key = std::string(Options::FIELDS_PREFIX) + "." + field_name + "." +
std::string(Options::LIST_AGG_DELIMITER);
PAIMON_RETURN_NOT_OK(parser.Parse(key, &delimiter));
return delimiter;
}
Result<bool> CoreOptions::FieldCollectAggDistinct(const std::string& field_name) const {
ConfigParser parser(impl_->raw_options);
bool distinct = false;
std::string key = std::string(Options::FIELDS_PREFIX) + "." + field_name + "." +
std::string(Options::DISTINCT);
PAIMON_RETURN_NOT_OK(parser.Parse<bool>(key, &distinct));
return distinct;
}
bool CoreOptions::DeletionVectorsEnabled() const {
return impl_->deletion_vectors_enabled;
}
bool CoreOptions::DeletionVectorsBitmap64() const {
return impl_->deletion_vectors_bitmap64;
}
int64_t CoreOptions::DeletionVectorTargetFileSize() const {
return impl_->deletion_vector_target_file_size;
}
ChangelogProducer CoreOptions::GetChangelogProducer() const {
return impl_->changelog_producer;
}
LookupStrategy CoreOptions::GetLookupStrategy() const {
return LookupStrategy::From(
/*is_first_row=*/GetMergeEngine() == MergeEngine::FIRST_ROW,
/*produce_changelog=*/GetChangelogProducer() == ChangelogProducer::LOOKUP,
/*deletion_vector=*/DeletionVectorsEnabled(),
/*force_lookup=*/impl_->force_lookup);
}
const std::map<std::string, std::string>& CoreOptions::ToMap() const {
return impl_->raw_options;
}
bool CoreOptions::NeedLookup() const {
return GetLookupStrategy().need_lookup;
}
bool CoreOptions::PrepareCommitWaitCompaction() const {
if (!NeedLookup()) {
return false;
}
return impl_->lookup_wait;
}
bool CoreOptions::CompactionForceRewriteAllFiles() const {
return impl_->compaction_force_rewrite_all_files;
}
bool CoreOptions::CompactionForceUpLevel0() const {
return impl_->compaction_force_up_level_0;
}
std::map<std::string, std::string> CoreOptions::GetFieldsSequenceGroups() const {
auto raw_options = impl_->raw_options;
std::map<std::string, std::string> sequence_groups;
for (const auto& [key, value] : raw_options) {
if (StringUtils::StartsWith(key, Options::FIELDS_PREFIX, /*start_pos=*/0) &&
StringUtils::EndsWith(key, Options::SEQUENCE_GROUP)) {
std::string seq_fields_str =
key.substr(std::strlen(Options::FIELDS_PREFIX) + 1,
key.size() - std::strlen(Options::FIELDS_PREFIX) -
std::strlen(Options::SEQUENCE_GROUP) - 2);
sequence_groups[seq_fields_str] = value;
}
}
return sequence_groups;
}
bool CoreOptions::PartialUpdateRemoveRecordOnDelete() const {
return impl_->partial_update_remove_record_on_delete;
}
bool CoreOptions::AggregationRemoveRecordOnDelete() const {
return impl_->aggregation_remove_record_on_delete;
}
bool CoreOptions::TableReadSequenceNumberEnabled() const {
return impl_->table_read_sequence_number_enabled;
}
bool CoreOptions::KeyValueSequenceNumberEnabled() const {
return impl_->key_value_sequence_number_enabled;
}
std::vector<std::string> CoreOptions::GetPartialUpdateRemoveRecordOnSequenceGroup() const {
return impl_->remove_record_on_sequence_group;
}
std::optional<std::string> CoreOptions::GetScanFallbackBranch() const {
return impl_->scan_fallback_branch;
}
std::string CoreOptions::GetBranch() const {
return impl_->branch;
}
bool CoreOptions::FileIndexReadEnabled() const {
return impl_->file_index_read_enabled;
}
std::optional<std::string> CoreOptions::GetDataFileExternalPaths() const {
return impl_->data_file_external_paths;
}
ExternalPathStrategy CoreOptions::GetExternalPathStrategy() const {
return impl_->external_path_strategy;
}
Result<std::vector<std::string>> CoreOptions::CreateExternalPaths() const {
std::vector<std::string> external_paths;
std::optional<std::string> data_file_external_paths = GetDataFileExternalPaths();
ExternalPathStrategy strategy = GetExternalPathStrategy();
if (strategy == ExternalPathStrategy::SPECIFIC_FS) {
return Status::NotImplemented("do not support specific-fs external path strategy for now");
}
if (data_file_external_paths == std::nullopt || data_file_external_paths->empty() ||
strategy == ExternalPathStrategy::NONE) {
return external_paths;
}
for (const auto& p : StringUtils::Split(data_file_external_paths.value(), ",")) {
std::string tmp_path = p;
StringUtils::Trim(&tmp_path);
PAIMON_ASSIGN_OR_RAISE(Path path, PathUtil::ToPath(tmp_path));
if (path.scheme.empty()) {
return Status::Invalid(fmt::format("scheme is null, path is {}", p));
}
external_paths.push_back(path.ToString());
}
if (external_paths.empty()) {
return Status::Invalid("external paths is empty");
}
return external_paths;
}
std::string CoreOptions::DataFilePrefix() const {
return impl_->data_file_prefix;
}
bool CoreOptions::IndexFileInDataFileDir() const {
return impl_->index_file_in_data_file_dir;
}
bool CoreOptions::RowTrackingEnabled() const {
return impl_->row_tracking_enabled;
}
bool CoreOptions::RowTrackingPartitionGroupOnCommit() const {
return impl_->row_tracking_partition_group_on_commit;
}
bool CoreOptions::DataEvolutionEnabled() const {
return impl_->data_evolution_enabled;
}
bool CoreOptions::LegacyPartitionNameEnabled() const {
return impl_->legacy_partition_name_enabled;
}
bool CoreOptions::GlobalIndexEnabled() const {
return impl_->global_index_enabled;
}
std::optional<int32_t> CoreOptions::GetGlobalIndexThreadNum() const {
return impl_->global_index_thread_num;
}
std::optional<std::string> CoreOptions::GetGlobalIndexExternalPath() const {
return impl_->global_index_external_path;
}
Result<std::optional<std::string>> CoreOptions::CreateGlobalIndexExternalPath() const {
std::optional<std::string> global_index_external_path = GetGlobalIndexExternalPath();
if (global_index_external_path == std::nullopt || global_index_external_path->empty()) {
return std::optional<std::string>();
}
std::string tmp_path = global_index_external_path.value();
StringUtils::Trim(&tmp_path);
PAIMON_ASSIGN_OR_RAISE(Path path, PathUtil::ToPath(tmp_path));
if (path.scheme.empty()) {
return Status::Invalid(fmt::format("scheme is null, path is {}", tmp_path));
}
return std::optional<std::string>(path.ToString());
}
std::optional<std::string> CoreOptions::GetScanTagName() const {
return impl_->scan_tag_name;
}
std::optional<int64_t> CoreOptions::GetOptimizedCompactionInterval() const {
return impl_->optimized_compaction_interval;
}
std::optional<int64_t> CoreOptions::GetCompactionTotalSizeThreshold() const {
return impl_->compaction_total_size_threshold;
}
std::optional<int64_t> CoreOptions::GetCompactionIncrementalSizeThreshold() const {
return impl_->compaction_incremental_size_threshold;
}
int32_t CoreOptions::GetCompactOffPeakStartHour() const {
return impl_->compact_off_peak_start_hour;
}
int32_t CoreOptions::GetCompactOffPeakEndHour() const {
return impl_->compact_off_peak_end_hour;
}
int32_t CoreOptions::GetCompactOffPeakRatio() const {
return impl_->compact_off_peak_ratio;
}
bool CoreOptions::LookupCacheBloomFilterEnabled() const {
return impl_->lookup_cache_bloom_filter;
}
double CoreOptions::GetLookupCacheBloomFilterFpp() const {
return impl_->lookup_cache_bloom_filter_fpp;
}
const CompressOptions& CoreOptions::GetLookupCompressOptions() const {
return impl_->lookup_compress_options;
}
bool CoreOptions::LookupRemoteFileEnabled() const {
return impl_->lookup_remote_file_enabled;
}
int32_t CoreOptions::GetLookupRemoteLevelThreshold() const {
return impl_->lookup_remote_level_threshold;
}
int32_t CoreOptions::GetCachePageSize() const {
return static_cast<int32_t>(impl_->cache_page_size);
}
int64_t CoreOptions::GetLookupCacheMaxMemory() const {
return impl_->lookup_cache_max_memory;
}
double CoreOptions::GetLookupCacheHighPrioPoolRatio() const {
return impl_->lookup_cache_high_prio_pool_ratio;
}
BucketFunctionType CoreOptions::GetBucketFunctionType() const {
return impl_->bucket_function_type;
}
const std::vector<std::string>& CoreOptions::GetBlobFields() const {
return impl_->blob_fields;
}
const std::vector<std::string>& CoreOptions::GetBlobDescriptorFields() const {
return impl_->blob_descriptor_fields;
}
const std::vector<std::string>& CoreOptions::GetBlobViewFields() const {
return impl_->blob_view_fields;
}
std::vector<std::string> CoreOptions::GetBlobInlineFields() const {
std::vector<std::string> blob_inline_fields = impl_->blob_descriptor_fields;
blob_inline_fields.insert(blob_inline_fields.end(), impl_->blob_view_fields.begin(),
impl_->blob_view_fields.end());
return blob_inline_fields;
}
const std::vector<std::string>& CoreOptions::GetBlobExternalStorageFields() const {
return impl_->blob_external_storage_fields;
}
std::optional<std::string> CoreOptions::GetBlobExternalStoragePath() const {
return impl_->blob_external_storage_path;
}
int64_t CoreOptions::GetLookupCacheFileRetentionMs() const {
return impl_->lookup_cache_file_retention_ms;
}
int64_t CoreOptions::GetLookupCacheMaxDiskSize() const {
return impl_->lookup_cache_max_disk_size;
}
} // namespace paimon