|
25 | 25 | #include <utility> |
26 | 26 |
|
27 | 27 | #include "arrow/api.h" |
| 28 | +#include "arrow/ipc/json_simple.h" |
28 | 29 | #include "gtest/gtest.h" |
29 | 30 | #include "paimon/common/data/binary_row.h" |
30 | 31 | #include "paimon/common/data/data_define.h" |
31 | 32 | #include "paimon/core/io/data_file_meta.h" |
32 | 33 | #include "paimon/core/manifest/file_kind.h" |
33 | 34 | #include "paimon/core/manifest/file_source.h" |
34 | 35 | #include "paimon/core/manifest/manifest_entry.h" |
| 36 | +#include "paimon/core/manifest/manifest_entry_serializer.h" |
35 | 37 | #include "paimon/core/manifest/manifest_file_meta.h" |
36 | 38 | #include "paimon/core/stats/simple_stats.h" |
37 | 39 | #include "paimon/core/utils/file_store_path_factory.h" |
|
40 | 42 | #include "paimon/defs.h" |
41 | 43 | #include "paimon/format/file_format.h" |
42 | 44 | #include "paimon/format/file_format_factory.h" |
| 45 | +#include "paimon/format/format_writer.h" |
| 46 | +#include "paimon/format/writer_builder.h" |
43 | 47 | #include "paimon/fs/local/local_file_system.h" |
44 | 48 | #include "paimon/memory/memory_pool.h" |
45 | 49 | #include "paimon/testing/utils/binary_row_generator.h" |
@@ -100,38 +104,52 @@ class CountingFileSystem : public FileSystem { |
100 | 104 |
|
101 | 105 | class ManifestFileTest : public testing::Test { |
102 | 106 | public: |
103 | | - std::vector<ManifestEntry> ReadManifestEntry( |
| 107 | + Result<std::vector<ManifestEntry>> TryReadManifestEntry( |
104 | 108 | const std::string& file_format_str, const std::string& root_path, |
105 | 109 | const std::string& file_name, const std::shared_ptr<MemoryPool>& pool, |
106 | | - const std::optional<int32_t>& bucket = std::nullopt) const { |
| 110 | + const std::optional<int32_t>& bucket = std::nullopt, bool cache_enabled = false) const { |
107 | 111 | std::shared_ptr<FileSystem> file_system = std::make_shared<LocalFileSystem>(); |
108 | | - EXPECT_OK_AND_ASSIGN(std::shared_ptr<FileFormat> file_format, |
109 | | - FileFormatFactory::Get(file_format_str, {})); |
| 112 | + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileFormat> file_format, |
| 113 | + FileFormatFactory::Get(file_format_str, {})); |
110 | 114 | auto unused_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())})); |
111 | | - EXPECT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory, |
112 | | - FileStorePathFactory::Create( |
113 | | - root_path, unused_schema, /*partition_keys=*/{}, |
114 | | - /*default_part_value=*/"", file_format->Identifier(), |
115 | | - /*data_file_prefix=*/"data-", |
116 | | - /*legacy_partition_name_enabled=*/true, /*external_paths=*/{}, |
117 | | - /*global_index_external_path=*/std::nullopt, |
118 | | - /*index_file_in_data_file_dir=*/false, pool)); |
119 | | - EXPECT_OK_AND_ASSIGN(CoreOptions options, |
120 | | - CoreOptions::FromMap({{Options::FILE_FORMAT, "orc"}})); |
121 | | - EXPECT_OK_AND_ASSIGN( |
| 115 | + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileStorePathFactory> path_factory, |
| 116 | + FileStorePathFactory::Create( |
| 117 | + root_path, unused_schema, /*partition_keys=*/{}, |
| 118 | + /*default_part_value=*/"", file_format->Identifier(), |
| 119 | + /*data_file_prefix=*/"data-", |
| 120 | + /*legacy_partition_name_enabled=*/true, /*external_paths=*/{}, |
| 121 | + /*global_index_external_path=*/std::nullopt, |
| 122 | + /*index_file_in_data_file_dir=*/false, pool)); |
| 123 | + PAIMON_ASSIGN_OR_RAISE(CoreOptions options, |
| 124 | + CoreOptions::FromMap({{Options::FILE_FORMAT, "orc"}})); |
| 125 | + if (cache_enabled) { |
| 126 | + options.WithCache( |
| 127 | + std::make_shared<CountingRoutingCache>(CacheKind::MANIFEST, 64 * 1024 * 1024)); |
| 128 | + } |
| 129 | + PAIMON_ASSIGN_OR_RAISE( |
122 | 130 | std::unique_ptr<ManifestFile> manifest_file, |
123 | 131 | ManifestFile::Create(file_system, file_format, "zstd", path_factory, |
124 | 132 | /*target_file_size=*/1024, pool, options, unused_schema)); |
125 | 133 | std::vector<ManifestEntry> manifest_entries; |
126 | 134 | if (bucket) { |
127 | | - EXPECT_OK( |
| 135 | + PAIMON_RETURN_NOT_OK( |
128 | 136 | manifest_file->ReadBucketEntries(file_name, bucket.value(), &manifest_entries)); |
129 | 137 | } else { |
130 | | - EXPECT_OK(manifest_file->Read(file_name, /*filter=*/nullptr, &manifest_entries)); |
| 138 | + PAIMON_RETURN_NOT_OK( |
| 139 | + manifest_file->Read(file_name, /*filter=*/nullptr, &manifest_entries)); |
131 | 140 | } |
132 | 141 |
|
133 | 142 | return manifest_entries; |
134 | 143 | } |
| 144 | + |
| 145 | + std::vector<ManifestEntry> ReadManifestEntry( |
| 146 | + const std::string& file_format_str, const std::string& root_path, |
| 147 | + const std::string& file_name, const std::shared_ptr<MemoryPool>& pool, |
| 148 | + const std::optional<int32_t>& bucket = std::nullopt) const { |
| 149 | + EXPECT_OK_AND_ASSIGN(auto manifest_entries, TryReadManifestEntry(file_format_str, root_path, |
| 150 | + file_name, pool, bucket)); |
| 151 | + return manifest_entries; |
| 152 | + } |
135 | 153 | }; |
136 | 154 |
|
137 | 155 | TEST_F(ManifestFileTest, TestSimple) { |
@@ -373,45 +391,97 @@ TEST_F(ManifestFileTest, TestReadBucketEntriesSkipsDeserializingOtherBuckets) { |
373 | 391 | "manifest-3a44a0da-1008-463c-914e-28d271375e24-0", pool); |
374 | 392 | ASSERT_EQ(2, source_entries.size()); |
375 | 393 |
|
376 | | - auto test_dir = UniqueTestDirectory::Create(); |
377 | | - ASSERT_TRUE(test_dir); |
378 | | - std::shared_ptr<FileSystem> file_system = test_dir->GetFileSystem(); |
379 | | - ASSERT_OK(file_system->Mkdirs(FileStorePathFactory::ManifestPath(test_dir->Str()))); |
380 | | - ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileFormat> file_format, |
381 | | - FileFormatFactory::Get("avro", {})); |
382 | | - auto unused_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())})); |
383 | | - ASSERT_OK_AND_ASSIGN( |
384 | | - std::shared_ptr<FileStorePathFactory> path_factory, |
385 | | - FileStorePathFactory::Create(test_dir->Str(), unused_schema, /*partition_keys=*/{}, |
386 | | - /*default_part_value=*/"", file_format->Identifier(), |
387 | | - /*data_file_prefix=*/"data-", |
388 | | - /*legacy_partition_name_enabled=*/true, /*external_paths=*/{}, |
389 | | - /*global_index_external_path=*/std::nullopt, |
390 | | - /*index_file_in_data_file_dir=*/false, pool)); |
391 | | - ASSERT_OK_AND_ASSIGN(CoreOptions options, |
392 | | - CoreOptions::FromMap({{Options::FILE_FORMAT, "orc"}})); |
393 | | - ASSERT_OK_AND_ASSIGN( |
394 | | - std::unique_ptr<ManifestFile> manifest_file, |
395 | | - ManifestFile::Create(file_system, file_format, "zstd", path_factory, |
396 | | - /*target_file_size=*/1024, pool, options, unused_schema)); |
397 | | - |
398 | | - ManifestEntry invalid_other_bucket(FileKind(static_cast<int8_t>(2)), |
399 | | - source_entries[0].Partition(), /*bucket=*/1, |
400 | | - /*total_buckets=*/2, source_entries[0].File()); |
401 | | - ManifestEntry valid_target_bucket(FileKind::Add(), source_entries[1].Partition(), /*bucket=*/0, |
402 | | - /*total_buckets=*/2, source_entries[1].File()); |
403 | | - using WrittenFile = std::pair<std::string, int64_t>; |
404 | | - ASSERT_OK_AND_ASSIGN( |
405 | | - WrittenFile written_file, |
406 | | - manifest_file->WriteWithoutRolling({invalid_other_bucket, valid_target_bucket})); |
| 394 | + for (bool cache_enabled : {false, true}) { |
| 395 | + SCOPED_TRACE(cache_enabled); |
| 396 | + const std::string format = "avro"; |
| 397 | + auto test_dir = UniqueTestDirectory::Create(); |
| 398 | + ASSERT_TRUE(test_dir); |
| 399 | + std::shared_ptr<FileSystem> file_system = test_dir->GetFileSystem(); |
| 400 | + ASSERT_OK(file_system->Mkdirs(FileStorePathFactory::ManifestPath(test_dir->Str()))); |
| 401 | + ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileFormat> file_format, |
| 402 | + FileFormatFactory::Get(format, {})); |
| 403 | + auto unused_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())})); |
| 404 | + ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileStorePathFactory> path_factory, |
| 405 | + FileStorePathFactory::Create( |
| 406 | + test_dir->Str(), unused_schema, /*partition_keys=*/{}, |
| 407 | + /*default_part_value=*/"", file_format->Identifier(), |
| 408 | + /*data_file_prefix=*/"data-", |
| 409 | + /*legacy_partition_name_enabled=*/true, /*external_paths=*/{}, |
| 410 | + /*global_index_external_path=*/std::nullopt, |
| 411 | + /*index_file_in_data_file_dir=*/false, pool)); |
| 412 | + ASSERT_OK_AND_ASSIGN(CoreOptions options, |
| 413 | + CoreOptions::FromMap({{Options::FILE_FORMAT, "orc"}})); |
| 414 | + if (cache_enabled) { |
| 415 | + options.WithCache( |
| 416 | + std::make_shared<CountingRoutingCache>(CacheKind::MANIFEST, 64 * 1024 * 1024)); |
| 417 | + } |
| 418 | + ASSERT_OK_AND_ASSIGN( |
| 419 | + std::unique_ptr<ManifestFile> manifest_file, |
| 420 | + ManifestFile::Create(file_system, file_format, "null", path_factory, |
| 421 | + /*target_file_size=*/1024, pool, options, unused_schema)); |
407 | 422 |
|
408 | | - std::vector<ManifestEntry> all_entries; |
409 | | - ASSERT_NOK_WITH_MSG(manifest_file->Read(written_file.first, /*filter=*/nullptr, &all_entries), |
410 | | - "Unsupported byte value 2 for file kind."); |
| 423 | + ManifestEntry invalid_other_bucket(FileKind(static_cast<int8_t>(2)), |
| 424 | + source_entries[0].Partition(), /*bucket=*/1, |
| 425 | + /*total_buckets=*/2, source_entries[0].File()); |
| 426 | + ManifestEntry valid_target_bucket(FileKind::Add(), source_entries[1].Partition(), |
| 427 | + /*bucket=*/0, |
| 428 | + /*total_buckets=*/2, source_entries[1].File()); |
| 429 | + using WrittenFile = std::pair<std::string, int64_t>; |
| 430 | + ASSERT_OK_AND_ASSIGN( |
| 431 | + WrittenFile written_file, |
| 432 | + manifest_file->WriteWithoutRolling({invalid_other_bucket, valid_target_bucket})); |
| 433 | + |
| 434 | + std::vector<ManifestEntry> all_entries; |
| 435 | + ASSERT_NOK_WITH_MSG( |
| 436 | + manifest_file->Read(written_file.first, /*filter=*/nullptr, &all_entries), |
| 437 | + "Unsupported byte value 2 for file kind."); |
| 438 | + |
| 439 | + std::vector<ManifestEntry> bucket_entries; |
| 440 | + ASSERT_OK( |
| 441 | + manifest_file->ReadBucketEntries(written_file.first, /*bucket=*/0, &bucket_entries)); |
| 442 | + ASSERT_EQ(std::vector<ManifestEntry>({valid_target_bucket}), bucket_entries); |
| 443 | + } |
| 444 | +} |
411 | 445 |
|
412 | | - std::vector<ManifestEntry> bucket_entries; |
413 | | - ASSERT_OK(manifest_file->ReadBucketEntries(written_file.first, /*bucket=*/0, &bucket_entries)); |
414 | | - ASSERT_EQ(std::vector<ManifestEntry>({valid_target_bucket}), bucket_entries); |
| 446 | +TEST_F(ManifestFileTest, TestBucketProbeValidatesVersionsOutsideSelectedBucket) { |
| 447 | + auto pool = GetDefaultPool(); |
| 448 | + ManifestEntrySerializer serializer(pool); |
| 449 | + auto type = |
| 450 | + arrow::struct_({arrow::field(serializer.GetDataType()->field(0)->name(), arrow::int32()), |
| 451 | + arrow::field(serializer.GetDataType()->field(3)->name(), arrow::int32())}); |
| 452 | + const std::vector<std::pair<std::string, std::string>> cases = { |
| 453 | + {R"([[999,1],[2,0]])", "Unsupported version: 999"}, |
| 454 | + {R"([[1,1],[2,0]])", "not compatible"}, |
| 455 | + {R"([[null,1],[2,0]])", "must not be null"}, |
| 456 | + {R"([[2,null],[2,0]])", "must not be null"}}; |
| 457 | + for (const auto& item : cases) { |
| 458 | + SCOPED_TRACE(item.first); |
| 459 | + auto dir = UniqueTestDirectory::Create(); |
| 460 | + ASSERT_TRUE(dir); |
| 461 | + auto fs = dir->GetFileSystem(); |
| 462 | + const std::string manifest_dir = FileStorePathFactory::ManifestPath(dir->Str()); |
| 463 | + ASSERT_OK(fs->Mkdirs(manifest_dir)); |
| 464 | + const std::string file_name = "manifest-invalid-version"; |
| 465 | + ASSERT_OK_AND_ASSIGN(std::shared_ptr<FileFormat> format, |
| 466 | + FileFormatFactory::Get("avro", {})); |
| 467 | + ArrowSchema c_schema; |
| 468 | + ASSERT_TRUE(arrow::ExportSchema(*arrow::schema(type->fields()), &c_schema).ok()); |
| 469 | + ASSERT_OK_AND_ASSIGN(std::shared_ptr<WriterBuilder> builder, |
| 470 | + format->CreateWriterBuilder(&c_schema, 2)); |
| 471 | + ASSERT_OK_AND_ASSIGN(std::shared_ptr<OutputStream> output, |
| 472 | + fs->Create(manifest_dir + "/" + file_name, false)); |
| 473 | + ASSERT_OK_AND_ASSIGN(std::unique_ptr<FormatWriter> writer, builder->Build(output, "null")); |
| 474 | + auto array = arrow::ipc::internal::json::ArrayFromJSON(type, item.first).ValueOrDie(); |
| 475 | + ArrowArray c_array; |
| 476 | + ASSERT_TRUE(arrow::ExportArray(*array, &c_array).ok()); |
| 477 | + ASSERT_OK(writer->AddBatch(&c_array)); |
| 478 | + ASSERT_OK(writer->Flush()); |
| 479 | + ASSERT_OK(writer->Finish()); |
| 480 | + ASSERT_OK(output->Close()); |
| 481 | + ASSERT_NOK_WITH_MSG(TryReadManifestEntry("avro", dir->Str(), file_name, pool, /*bucket=*/0, |
| 482 | + /*cache_enabled=*/true), |
| 483 | + item.second); |
| 484 | + } |
415 | 485 | } |
416 | 486 |
|
417 | 487 | TEST_F(ManifestFileTest, TestLegacyManifestFormatIsReadOnly) { |
|
0 commit comments