3636#include " paimon/common/utils/arrow/mem_utils.h"
3737#include " paimon/common/utils/date_time_utils.h"
3838#include " paimon/common/utils/path_util.h"
39+ #include " paimon/format/file_format.h"
40+ #include " paimon/format/file_format_factory.h"
3941#include " paimon/format/parquet/parquet_field_id_converter.h"
4042#include " paimon/format/parquet/parquet_format_defs.h"
4143#include " paimon/fs/file_system.h"
@@ -84,8 +86,8 @@ class ParquetFormatWriterTest : public ::testing::Test {
8486 }
8587
8688 std::shared_ptr<arrow::Array> PrepareArray (const std::shared_ptr<arrow::DataType>& data_type,
87- int32_t record_batch_size,
88- int32_t offset = 0 ) const {
89+ int32_t record_batch_size, int32_t offset = 0 ,
90+ bool all_null_value = false ) const {
8991 arrow::StructBuilder struct_builder (
9092 data_type, arrow::default_memory_pool (),
9193 {std::make_shared<arrow::StringBuilder>(), std::make_shared<arrow::Int32Builder>(),
@@ -95,24 +97,31 @@ class ParquetFormatWriterTest : public ::testing::Test {
9597 auto bool_builder = static_cast <arrow::BooleanBuilder*>(struct_builder.field_builder (2 ));
9698 for (int32_t i = 0 + offset; i < record_batch_size + offset; ++i) {
9799 EXPECT_TRUE (struct_builder.Append ().ok ());
98- EXPECT_TRUE (string_builder->Append (" str_" + std::to_string (i)).ok ());
99- if (i % 3 == 0 ) {
100- // test null
100+ if (all_null_value) {
101+ EXPECT_TRUE (string_builder->AppendNull ().ok ());
101102 EXPECT_TRUE (int_builder->AppendNull ().ok ());
103+ EXPECT_TRUE (bool_builder->AppendNull ().ok ());
102104 } else {
103- EXPECT_TRUE (int_builder->Append (i).ok ());
105+ EXPECT_TRUE (string_builder->Append (" str_" + std::to_string (i)).ok ());
106+ if (i % 3 == 0 ) {
107+ // test null
108+ EXPECT_TRUE (int_builder->AppendNull ().ok ());
109+ } else {
110+ EXPECT_TRUE (int_builder->Append (i).ok ());
111+ }
112+ EXPECT_TRUE (bool_builder->Append (static_cast <bool >(i % 2 )).ok ());
104113 }
105- EXPECT_TRUE (bool_builder->Append (static_cast <bool >(i % 2 )).ok ());
106114 }
107115 std::shared_ptr<arrow::Array> array;
108116 EXPECT_TRUE (struct_builder.Finish (&array).ok ());
109117 return array;
110118 }
111119
112- void AddRecordBatchOnce (const std::shared_ptr<ParquetFormatWriter >& format_writer,
120+ void AddRecordBatchOnce (const std::shared_ptr<FormatWriter >& format_writer,
113121 const std::shared_ptr<arrow::DataType>& struct_type,
114- int32_t record_batch_size, int32_t offset) const {
115- auto array = PrepareArray (struct_type, record_batch_size, offset);
122+ int32_t record_batch_size, int32_t offset,
123+ bool all_null_value = false ) const {
124+ auto array = PrepareArray (struct_type, record_batch_size, offset, all_null_value);
116125 auto arrow_array = std::make_unique<ArrowArray>();
117126 ASSERT_TRUE (arrow::ExportArray (*array, arrow_array.get ()).ok ());
118127 auto batch = std::make_shared<RecordBatch>(
@@ -196,7 +205,8 @@ TEST_F(ParquetFormatWriterTest, TestWriteWithVariousBatchSize) {
196205 auto writer_properties = builder.build ();
197206 ASSERT_OK_AND_ASSIGN (
198207 auto format_writer,
199- ParquetFormatWriter::Create (out, arrow_schema, writer_properties, arrow_pool_));
208+ ParquetFormatWriter::Create (out, arrow_schema, writer_properties, arrow_pool_,
209+ DEFAULT_PARQUET_WRITER_MAX_MEMORY_USE ));
200210 auto array = PrepareArray (struct_type, record_batch_size);
201211 auto arrow_array = std::make_unique<ArrowArray>();
202212 ASSERT_TRUE (arrow::ExportArray (*array, arrow_array.get ()).ok ());
@@ -229,7 +239,8 @@ TEST_F(ParquetFormatWriterTest, TestWriteMultipleTimes) {
229239 auto writer_properties = builder.build ();
230240 ASSERT_OK_AND_ASSIGN (
231241 std::shared_ptr<ParquetFormatWriter> format_writer,
232- ParquetFormatWriter::Create (out, arrow_schema, writer_properties, arrow_pool_));
242+ ParquetFormatWriter::Create (out, arrow_schema, writer_properties, arrow_pool_,
243+ DEFAULT_PARQUET_WRITER_MAX_MEMORY_USE ));
233244
234245 // add batch first time, 6 rows
235246 AddRecordBatchOnce (format_writer, struct_type, 6 , 0 );
@@ -271,7 +282,8 @@ TEST_F(ParquetFormatWriterTest, TestGetEstimateLength) {
271282 auto writer_properties = builder.build ();
272283 ASSERT_OK_AND_ASSIGN (
273284 std::shared_ptr<ParquetFormatWriter> format_writer,
274- ParquetFormatWriter::Create (out, arrow_schema, writer_properties, arrow_pool_));
285+ ParquetFormatWriter::Create (out, arrow_schema, writer_properties, arrow_pool_,
286+ DEFAULT_PARQUET_WRITER_MAX_MEMORY_USE ));
275287
276288 // add batch first time, 1 row
277289 AddRecordBatchOnce (format_writer, struct_type, 1 , 0 );
@@ -289,6 +301,59 @@ TEST_F(ParquetFormatWriterTest, TestGetEstimateLength) {
289301 ASSERT_TRUE (format_writer->Finish ().ok ());
290302}
291303
304+ TEST_F (ParquetFormatWriterTest, TestMemoryControl) {
305+ auto check_result = [&](bool all_null_value, uint64_t max_memory_use) {
306+ ASSERT_OK_AND_ASSIGN (
307+ std::unique_ptr<FileFormat> file_format,
308+ FileFormatFactory::Get (
309+ " parquet" , {{Options::FILE_FORMAT , " parquet" },
310+ {Options::MANIFEST_FORMAT , " parquet" },
311+ {" parquet.writer.max.memory.use" , std::to_string (max_memory_use)}}));
312+
313+ std::shared_ptr<MemoryPool> pool = GetMemoryPool ();
314+ auto schema_pair = PrepareArrowSchema ();
315+ const auto & arrow_schema = schema_pair.first ;
316+ const auto & struct_type = schema_pair.second ;
317+ int32_t batch_size = 4096 ;
318+
319+ auto c_schema = std::make_unique<::ArrowSchema>();
320+ ASSERT_TRUE (arrow::ExportSchema (*arrow_schema, c_schema.get ()).ok ());
321+ ASSERT_OK_AND_ASSIGN (auto writer_builder,
322+ file_format->CreateWriterBuilder (c_schema.get (), batch_size));
323+ ASSERT_OK_AND_ASSIGN (
324+ std::shared_ptr<OutputStream> out,
325+ fs_->Create (
326+ PathUtil::JoinPath (dir_->Str (), std::to_string (all_null_value) +
327+ std::to_string (max_memory_use) + " .parquet" ),
328+ /* overwrite=*/ false ));
329+ ASSERT_OK_AND_ASSIGN (std::shared_ptr<FormatWriter> writer,
330+ writer_builder->WithMemoryPool (pool)->Build (out, " uncompressed" ));
331+
332+ auto array = PrepareArray (struct_type, batch_size, /* offset=*/ 0 , all_null_value);
333+ for (int32_t i = 0 ; i < 2000 ; ++i) {
334+ auto arrow_array = std::make_unique<ArrowArray>();
335+ ASSERT_TRUE (arrow::ExportArray (*array, arrow_array.get ()).ok ());
336+ auto batch = std::make_shared<RecordBatch>(
337+ /* partition=*/ std::map<std::string, std::string>(), /* bucket=*/ -1 ,
338+ /* row_kinds=*/ std::vector<RecordBatch::RowKind>(), arrow_array.get ());
339+ ASSERT_OK (writer->AddBatch (batch->GetData ()));
340+ ASSERT_OK (writer->Flush ());
341+ }
342+
343+ ASSERT_OK (writer->Flush ());
344+ ASSERT_OK (writer->Finish ());
345+ ASSERT_OK (out->Flush ());
346+ ASSERT_OK (out->Close ());
347+ uint64_t actual_max_mem = pool->MaxMemoryUsage ();
348+ ASSERT_GT (actual_max_mem, max_memory_use);
349+ ASSERT_LT (actual_max_mem, max_memory_use * 1.5 ); // allow 50% overhead
350+ };
351+ check_result (/* all_null_value=*/ true , /* max_memory_use=*/ 20 * 1024 * 1024 ); // 20MB
352+ check_result (/* all_null_value=*/ true , /* max_memory_use=*/ 40 * 1024 * 1024 ); // 40MB
353+ check_result (/* all_null_value=*/ false , /* max_memory_use=*/ 20 * 1024 * 1024 ); // 20MB
354+ check_result (/* all_null_value=*/ false , /* max_memory_use=*/ 40 * 1024 * 1024 ); // 40MB
355+ }
356+
292357TEST_F (ParquetFormatWriterTest, TestTimestampType) {
293358 auto timezone = DateTimeUtils::GetLocalTimezoneName ();
294359 arrow::FieldVector fields = {
@@ -298,15 +363,17 @@ TEST_F(ParquetFormatWriterTest, TestTimestampType) {
298363 arrow::field (" ts_nano" , arrow::timestamp (arrow::TimeUnit::NANO )),
299364 arrow::field (" ts_utc1" , arrow::timestamp (arrow::TimeUnit::SECOND , timezone)),
300365 arrow::field (" ts_utc2" , arrow::timestamp (arrow::TimeUnit::MICRO , timezone))};
366+ [[maybe_unused]] auto test = new arrow::Schema (fields); // for test
301367
302368 std::string file_path = PathUtil::JoinPath (dir_->Str (), " timezone.parquet" );
303369 ASSERT_OK_AND_ASSIGN (std::shared_ptr<OutputStream> out,
304370 fs_->Create (file_path, /* overwrite=*/ true ));
305371 ::parquet::WriterProperties::Builder builder;
306372 auto writer_properties = builder.build ();
307- ASSERT_OK_AND_ASSIGN (std::shared_ptr<ParquetFormatWriter> format_writer,
308- ParquetFormatWriter::Create (out, std::make_shared<arrow::Schema>(fields),
309- writer_properties, arrow_pool_));
373+ ASSERT_OK_AND_ASSIGN (
374+ std::shared_ptr<ParquetFormatWriter> format_writer,
375+ ParquetFormatWriter::Create (out, std::make_shared<arrow::Schema>(fields), writer_properties,
376+ arrow_pool_, DEFAULT_PARQUET_WRITER_MAX_MEMORY_USE ));
310377
311378 auto array = std::dynamic_pointer_cast<arrow::StructArray>(
312379 arrow::ipc::internal::json::ArrayFromJSON (arrow::struct_ (fields), R"( [
0 commit comments