Skip to content

Commit ad5d80d

Browse files
committed
feat(mergetree): support spill-to-disk write buffer and writer memory manager
Introduce ExternalSortBuffer that wraps an InMemorySortBuffer and spills sorted runs to disk via IOManager channels when the in-memory budget is reached. Spilled runs are merged back through the existing sort-merge reader path on flush. Also add WriterMemoryManager to coordinate write-buffer memory across multiple BatchWriters by picking the largest writer to flush when the global budget is exceeded. New CoreOptions: - write-buffer-spillable - write-buffer-spill.max-disk-size - local-sort.max-num-file-handles - spill-compression - spill-compression.zstd-level (moved next to other spill options) Tests: - sort_buffer_test: external sort buffer spill & merge behaviour - writer_memory_manager_test: memory accounting and shrink-to-limit - extend sort_merge_reader_test / core_options_test / data_generator_test Generated-by: Aone Copilot (claude-4.7-opus)
1 parent c7492e6 commit ad5d80d

20 files changed

Lines changed: 1516 additions & 249 deletions

include/paimon/defs.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,27 @@ struct PAIMON_EXPORT Options {
178178
/// on-disk file. The default value is 256 mb
179179
static const char WRITE_BUFFER_SIZE[];
180180

181+
/// "write-buffer-spillable" - Whether the write buffer can be spillable. Default value is true.
182+
static const char WRITE_BUFFER_SPILLABLE[];
183+
184+
/// "write-buffer-spill.max-disk-size" - The max disk to use for write buffer spill. This only
185+
/// work when the write buffer spill is enabled. Default value is unlimited.
186+
static const char WRITE_BUFFER_SPILL_MAX_DISK_SIZE[];
187+
188+
/// "local-sort.max-num-file-handles" - The maximal fan-in for external merge sort. It limits
189+
/// the number of file handles. If it is too small, may cause intermediate merging. But if it is
190+
/// too large, it will cause too many files opened at the same time, consume memory and lead to
191+
/// random reading. Default value is 128.
192+
static const char LOCAL_SORT_MAX_NUM_FILE_HANDLES[];
193+
194+
/// "spill-compression" - Compression for spill. Default value is zstd.
195+
static const char SPILL_COMPRESSION[];
196+
197+
/// "spill-compression.zstd-level" - Default spill compression zstd level. For higher
198+
/// compression rates, it can be configured to 9, but the read and write speed will
199+
/// significantly decrease. Default value is 1.
200+
static const char SPILL_COMPRESSION_ZSTD_LEVEL[];
201+
181202
/// "snapshot.num-retained.min" - The minimum number of completed snapshots to retain. Should be
182203
/// greater than or equal to 1. Default value is 10
183204
static const char SNAPSHOT_NUM_RETAINED_MIN[];
@@ -405,10 +426,6 @@ struct PAIMON_EXPORT Options {
405426
/// lz4 are supported. Default value is zstd.
406427
/// Noted that java paimon also supports lzo which paimon-cpp does not support for now.
407428
static const char LOOKUP_CACHE_SPILL_COMPRESSION[];
408-
/// "spill-compression.zstd-level" - Default spill compression zstd level. For higher
409-
/// compression rates, it can be configured to 9, but the read and write speed will
410-
/// significantly decrease. Default value is 1.
411-
static const char SPILL_COMPRESSION_ZSTD_LEVEL[];
412429
/// "cache-page-size" - Memory page size for caching. Default value is 64 kb.
413430
static const char CACHE_PAGE_SIZE[];
414431
/// "file.format.per.level" - Define different file format for different level, you can add the

src/paimon/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,9 @@ set(PAIMON_CORE_SRCS
251251
core/mergetree/compact/changelog_merge_tree_rewriter.cpp
252252
core/mergetree/merge_tree_writer.cpp
253253
core/mergetree/in_memory_sort_buffer.cpp
254+
core/mergetree/external_sort_buffer.cpp
254255
core/mergetree/write_buffer.cpp
256+
core/mergetree/writer_memory_manager.cpp
255257
core/mergetree/levels.cpp
256258
core/mergetree/lookup_file.cpp
257259
core/mergetree/lookup_levels.cpp
@@ -633,9 +635,11 @@ if(PAIMON_BUILD_TESTS)
633635
core/mergetree/drop_delete_reader_test.cpp
634636
core/mergetree/merge_tree_writer_test.cpp
635637
core/mergetree/write_buffer_test.cpp
638+
core/mergetree/sort_buffer_test.cpp
636639
core/mergetree/sorted_run_test.cpp
637640
core/mergetree/spill_channel_manager_test.cpp
638641
core/mergetree/spill_reader_writer_test.cpp
642+
core/mergetree/writer_memory_manager_test.cpp
639643
core/migrate/file_meta_utils_test.cpp
640644
core/operation/metrics/compaction_metrics_test.cpp
641645
core/operation/data_evolution_file_store_scan_test.cpp

src/paimon/common/defs.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ const char Options::SCAN_MODE[] = "scan.mode";
5050
const char Options::READ_BATCH_SIZE[] = "read.batch-size";
5151
const char Options::WRITE_BATCH_SIZE[] = "write.batch-size";
5252
const char Options::WRITE_BUFFER_SIZE[] = "write-buffer-size";
53+
const char Options::WRITE_BUFFER_SPILLABLE[] = "write-buffer-spillable";
54+
const char Options::WRITE_BUFFER_SPILL_MAX_DISK_SIZE[] = "write-buffer-spill.max-disk-size";
55+
const char Options::LOCAL_SORT_MAX_NUM_FILE_HANDLES[] = "local-sort.max-num-file-handles";
56+
const char Options::SPILL_COMPRESSION[] = "spill-compression";
57+
const char Options::SPILL_COMPRESSION_ZSTD_LEVEL[] = "spill-compression.zstd-level";
5358
const char Options::SNAPSHOT_NUM_RETAINED_MIN[] = "snapshot.num-retained.min";
5459
const char Options::SNAPSHOT_NUM_RETAINED_MAX[] = "snapshot.num-retained.max";
5560
const char Options::SNAPSHOT_TIME_RETAINED[] = "snapshot.time-retained";
@@ -104,7 +109,6 @@ const char Options::LOOKUP_CACHE_BLOOM_FILTER_FPP[] = "lookup.cache.bloom.filter
104109
const char Options::LOOKUP_REMOTE_FILE_ENABLED[] = "lookup.remote-file.enabled";
105110
const char Options::LOOKUP_REMOTE_LEVEL_THRESHOLD[] = "lookup.remote-file.level-threshold";
106111
const char Options::LOOKUP_CACHE_SPILL_COMPRESSION[] = "lookup.cache-spill-compression";
107-
const char Options::SPILL_COMPRESSION_ZSTD_LEVEL[] = "spill-compression.zstd-level";
108112
const char Options::CACHE_PAGE_SIZE[] = "cache-page-size";
109113
const char Options::FILE_FORMAT_PER_LEVEL[] = "file.format.per.level";
110114
const char Options::FILE_COMPRESSION_PER_LEVEL[] = "file.compression.per.level";

src/paimon/common/utils/string_utils_test.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,12 @@ TEST_F(StringUtilsTest, TestSplit) {
326326
StringUtils::Split("key1=value1//key3=value3", std::string("/"), std::string("="));
327327
ASSERT_EQ(expect, result);
328328
}
329+
{
330+
std::vector<std::vector<std::string>> expect = {};
331+
std::vector<std::vector<std::string>> result =
332+
StringUtils::Split("", std::string("/"), std::string("="));
333+
ASSERT_EQ(expect, result);
334+
}
329335
}
330336

331337
TEST_F(StringUtilsTest, TestStringToValueSimple) {

0 commit comments

Comments
 (0)