blob: b5694e440f85e8798de90bf3780a91a4bc6b2267 [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/snapshot.h"
#include "gtest/gtest.h"
#include "paimon/common/utils/string_utils.h"
#include "paimon/fs/local/local_file_system.h"
#include "paimon/result.h"
#include "paimon/snapshot/snapshot_info.h"
#include "paimon/status.h"
#include "paimon/testing/utils/testharness.h"
namespace paimon::test {
class SnapshotTest : public testing::Test {
public:
std::string ReplaceAll(const std::string& str) {
std::string replaced_str = StringUtils::Replace(str, " ", "");
replaced_str = StringUtils::Replace(replaced_str, "\t", "");
replaced_str = StringUtils::Replace(replaced_str, "\n", "");
return replaced_str;
}
};
TEST_F(SnapshotTest, TestSimple) {
std::map<int32_t, int64_t> log_offset = {{25, 30}};
std::map<std::string, std::string> properties = {{"key1", "value1"}, {"key2", "value2"}};
Snapshot snapshot(
/*version=*/5, /*id=*/10, /*schema_id=*/15, /*base_manifest_list=*/"base_manifest_list", 10,
/*delta_manifest_list=*/"delta_manifest_list", 20,
/*changelog_manifest_list=*/"changelog_manifest_list", 30,
/*index_manifest=*/"index_manifest",
/*commit_user=*/"commit_user_01", /*commit_identifier=*/20,
/*commit_kind=*/Snapshot::CommitKind::Compact(), /*time_millis=*/1234, log_offset,
/*total_record_count=*/35,
/*delta_record_count=*/40, /*changelog_record_count=*/45, /*watermark=*/50,
/*statistics=*/"statistic_test", properties, /*next_row_id=*/0);
ASSERT_EQ(5, snapshot.Version());
ASSERT_EQ(10, snapshot.Id());
ASSERT_EQ(15, snapshot.SchemaId());
ASSERT_EQ("base_manifest_list", snapshot.BaseManifestList());
ASSERT_EQ(10, snapshot.BaseManifestListSize().value());
ASSERT_EQ("delta_manifest_list", snapshot.DeltaManifestList());
ASSERT_EQ(20, snapshot.DeltaManifestListSize().value());
ASSERT_EQ("changelog_manifest_list", snapshot.ChangelogManifestList().value());
ASSERT_EQ(30, snapshot.ChangelogManifestListSize().value());
ASSERT_EQ("index_manifest", snapshot.IndexManifest().value());
ASSERT_EQ("commit_user_01", snapshot.CommitUser());
ASSERT_EQ(20, snapshot.CommitIdentifier());
ASSERT_EQ(Snapshot::CommitKind::Compact(), snapshot.GetCommitKind());
ASSERT_EQ(1234, snapshot.TimeMillis());
ASSERT_EQ(log_offset, snapshot.LogOffsets().value());
ASSERT_EQ(35, snapshot.TotalRecordCount().value());
ASSERT_EQ(40, snapshot.DeltaRecordCount().value());
ASSERT_EQ(45, snapshot.ChangelogRecordCount().value());
ASSERT_EQ(50, snapshot.Watermark().value());
ASSERT_EQ("statistic_test", snapshot.Statistics().value());
ASSERT_EQ(properties, snapshot.Properties().value());
ASSERT_EQ(0, snapshot.NextRowId().value());
}
TEST_F(SnapshotTest, TestFromPath) {
std::string data_path =
paimon::test::GetDataDir() + "/orc/append_09.db/append_09/snapshot/snapshot-1";
auto fs = std::make_shared<LocalFileSystem>();
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromPath(fs, data_path));
ASSERT_EQ(3, snapshot.Version());
ASSERT_EQ(1, snapshot.Id());
ASSERT_EQ(0, snapshot.SchemaId());
ASSERT_EQ("manifest-list-616d1847-a02c-495f-9cca-2c8b7def0fec-0", snapshot.BaseManifestList());
ASSERT_EQ(std::nullopt, snapshot.BaseManifestListSize());
ASSERT_EQ("manifest-list-616d1847-a02c-495f-9cca-2c8b7def0fec-1", snapshot.DeltaManifestList());
ASSERT_EQ(std::nullopt, snapshot.DeltaManifestListSize());
ASSERT_EQ(std::nullopt, snapshot.ChangelogManifestList());
ASSERT_EQ(std::nullopt, snapshot.ChangelogManifestListSize());
ASSERT_EQ(std::nullopt, snapshot.IndexManifest());
ASSERT_EQ("b02e4322-9c5f-41e1-a560-c0156fdf7b9c", snapshot.CommitUser());
ASSERT_EQ(9223372036854775807ll, snapshot.CommitIdentifier());
ASSERT_EQ(Snapshot::CommitKind::Append(), snapshot.GetCommitKind());
ASSERT_EQ(1721614343270ll, snapshot.TimeMillis());
ASSERT_EQ((std::map<int32_t, int64_t>()), snapshot.LogOffsets().value());
ASSERT_EQ(5, snapshot.TotalRecordCount().value());
ASSERT_EQ(5, snapshot.DeltaRecordCount().value());
ASSERT_EQ(0, snapshot.ChangelogRecordCount().value());
ASSERT_EQ(std::nullopt, snapshot.Watermark());
ASSERT_EQ(std::nullopt, snapshot.Statistics());
ASSERT_EQ(std::nullopt, snapshot.Properties());
ASSERT_EQ(std::nullopt, snapshot.NextRowId());
}
TEST_F(SnapshotTest, TestJsonizable) {
std::string json_str = R"({
"version" : 3,
"id" : 1,
"schemaId" : 0,
"baseManifestList" : "manifest-list-d96fcc30-99e8-4f45-962b-a1157c56f378-0",
"baseManifestListSize" : 20,
"deltaManifestList" : "manifest-list-d96fcc30-99e8-4f45-962b-a1157c56f378-1",
"deltaManifestListSize" : 50,
"changelogManifestList" : null,
"commitUser" : "0e4d92f7-53b0-40d6-a7c0-102bf3801e6a",
"commitIdentifier" : 9223372036854775807,
"commitKind" : "OVERWRITE",
"timeMillis" : 1711692199281,
"logOffsets" : { },
"totalRecordCount" : 3,
"deltaRecordCount" : 3,
"changelogRecordCount" : 0
})";
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromJsonString(json_str));
Snapshot expected_snapshot(
/*version=*/3, /*id=*/1, /*schema_id=*/0, /*base_manifest_list=*/
"manifest-list-d96fcc30-99e8-4f45-962b-a1157c56f378-0", /*base_manifest_list_size=*/20,
/*delta_manifest_list=*/"manifest-list-d96fcc30-99e8-4f45-962b-a1157c56f378-1",
/*delta_manifest_list_size=*/50, /*changelog_manifest_list=*/std::nullopt,
/*changelog_manifest_list_size=*/std::nullopt, /*index_manifest=*/std::nullopt,
/*commit_user=*/"0e4d92f7-53b0-40d6-a7c0-102bf3801e6a",
/*commit_identifier=*/9223372036854775807ll,
/*commit_kind=*/Snapshot::CommitKind::Overwrite(), /*time_millis=*/1711692199281ll,
/*log_offsets=*/std::map<int32_t, int64_t>(),
/*total_record_count=*/3, /*delta_record_count=*/3, /*changelog_record_count=*/0,
/*watermark=*/std::nullopt, /*statistics=*/std::nullopt, /*properties=*/std::nullopt,
/*next_row_id=*/std::nullopt);
ASSERT_EQ(expected_snapshot, snapshot);
ASSERT_OK_AND_ASSIGN(std::string new_json_str, snapshot.ToJsonString());
ASSERT_EQ(ReplaceAll(json_str), ReplaceAll(new_json_str));
}
TEST_F(SnapshotTest, TestSerializeAndDeserialize) {
auto se_and_de = [&](const std::string& data_path) {
auto fs = std::make_shared<LocalFileSystem>();
std::string json_str;
ASSERT_OK(fs->ReadFile(data_path, &json_str));
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromPath(fs, data_path));
ASSERT_EQ(snapshot, snapshot);
ASSERT_OK_AND_ASSIGN(std::string se_json_str, snapshot.ToJsonString());
ASSERT_EQ(ReplaceAll(json_str), ReplaceAll(se_json_str));
};
auto se_and_de_from_str = [&](const std::string& json_str) {
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromJsonString(json_str));
ASSERT_EQ(snapshot, snapshot);
ASSERT_OK_AND_ASSIGN(std::string se_json_str, snapshot.ToJsonString());
ASSERT_EQ(ReplaceAll(json_str), ReplaceAll(se_json_str));
};
{
// without indexManifest
std::string data_path =
paimon::test::GetDataDir() +
"/orc/pk_table_scan_and_read_dv.db/pk_table_scan_and_read_dv/snapshot/snapshot-1";
se_and_de(data_path);
}
{
// with indexManifest
std::string data_path =
paimon::test::GetDataDir() +
"/orc/pk_table_scan_and_read_dv.db/pk_table_scan_and_read_dv/snapshot/snapshot-6";
se_and_de(data_path);
}
{
// with ManifestListSize
std::string data_path = paimon::test::GetDataDir() +
"/orc/append_with_bsi_bitmap_bloomfilter.db/"
"append_with_bsi_bitmap_bloomfilter/snapshot/snapshot-1";
se_and_de(data_path);
}
{
// with properties
std::string json_str = R"({
"version" : 3,
"id" : 10,
"schemaId" : 2,
"baseManifestList" : "base-manifest-list-1",
"baseManifestListSize" : 100,
"deltaManifestList" : "delta-manifest-list-2",
"deltaManifestListSize" : 200,
"changelogManifestList" : null,
"commitUser" : "commit-usr-3",
"commitIdentifier" : 12,
"commitKind" : "APPEND",
"timeMillis" : 1749724197266,
"logOffsets" : {
"0" : 1,
"1" : 3
},
"totalRecordCount" : 1024,
"deltaRecordCount" : 4096,
"watermark" : 1749724196266,
"statistics" : "statistics-4",
"properties" : {
"key0" : "value0",
"key1" : "value1"
}
})";
se_and_de_from_str(json_str);
}
{
// with next_row_id
std::string json_str = R"({
"version" : 3,
"id" : 10,
"schemaId" : 2,
"baseManifestList" : "base-manifest-list-1",
"baseManifestListSize" : 100,
"deltaManifestList" : "delta-manifest-list-2",
"deltaManifestListSize" : 200,
"changelogManifestList" : null,
"commitUser" : "commit-usr-3",
"commitIdentifier" : 12,
"commitKind" : "APPEND",
"timeMillis" : 1749724197266,
"logOffsets" : {
"0" : 1,
"1" : 3
},
"totalRecordCount" : 1024,
"deltaRecordCount" : 4096,
"watermark" : 1749724196266,
"statistics" : "statistics-4",
"properties" : {
"key0" : "value0",
"key1" : "value1"
},
"nextRowId" : 0
})";
se_and_de_from_str(json_str);
}
}
TEST_F(SnapshotTest, TestCommitKindAnalyze) {
// Test constructing a Snapshot with CommitKind::Analyze
Snapshot snapshot(
/*version=*/3, /*id=*/20, /*schema_id=*/5,
/*base_manifest_list=*/"base-manifest-analyze",
/*base_manifest_list_size=*/100,
/*delta_manifest_list=*/"delta-manifest-analyze",
/*delta_manifest_list_size=*/200,
/*changelog_manifest_list=*/std::nullopt,
/*changelog_manifest_list_size=*/std::nullopt,
/*index_manifest=*/std::nullopt,
/*commit_user=*/"analyze-user",
/*commit_identifier=*/42,
/*commit_kind=*/Snapshot::CommitKind::Analyze(),
/*time_millis=*/1700000000000ll,
/*log_offsets=*/std::map<int32_t, int64_t>(),
/*total_record_count=*/0,
/*delta_record_count=*/0,
/*changelog_record_count=*/0,
/*watermark=*/std::nullopt,
/*statistics=*/"test-statistics",
/*properties=*/std::nullopt,
/*next_row_id=*/std::nullopt);
ASSERT_EQ(Snapshot::CommitKind::Analyze(), snapshot.GetCommitKind());
ASSERT_EQ("ANALYZE", Snapshot::CommitKind::ToString(snapshot.GetCommitKind()));
}
TEST_F(SnapshotTest, TestCommitKindAnalyzeSerializeAndDeserialize) {
std::string json_str = R"({
"version" : 3,
"id" : 20,
"schemaId" : 5,
"baseManifestList" : "base-manifest-analyze",
"baseManifestListSize" : 100,
"deltaManifestList" : "delta-manifest-analyze",
"deltaManifestListSize" : 200,
"changelogManifestList" : null,
"commitUser" : "analyze-user",
"commitIdentifier" : 42,
"commitKind" : "ANALYZE",
"timeMillis" : 1700000000000,
"logOffsets" : { },
"totalRecordCount" : 0,
"deltaRecordCount" : 0,
"changelogRecordCount" : 0,
"statistics" : "test-statistics"
})";
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromJsonString(json_str));
// Verify deserialization
ASSERT_EQ(20, snapshot.Id());
ASSERT_EQ(5, snapshot.SchemaId());
ASSERT_EQ(Snapshot::CommitKind::Analyze(), snapshot.GetCommitKind());
ASSERT_EQ("test-statistics", snapshot.Statistics().value());
// Verify round-trip serialization
ASSERT_OK_AND_ASSIGN(std::string serialized, snapshot.ToJsonString());
ASSERT_EQ(ReplaceAll(json_str), ReplaceAll(serialized));
// Verify re-deserialization produces equal snapshot
ASSERT_OK_AND_ASSIGN(Snapshot deserialized, Snapshot::FromJsonString(serialized));
ASSERT_EQ(snapshot, deserialized);
}
TEST_F(SnapshotTest, TestCommitKindToStringAndFromString) {
// Verify all CommitKind values round-trip through ToString/FromString
ASSERT_EQ("APPEND", Snapshot::CommitKind::ToString(Snapshot::CommitKind::Append()));
ASSERT_EQ("COMPACT", Snapshot::CommitKind::ToString(Snapshot::CommitKind::Compact()));
ASSERT_EQ("OVERWRITE", Snapshot::CommitKind::ToString(Snapshot::CommitKind::Overwrite()));
ASSERT_EQ("ANALYZE", Snapshot::CommitKind::ToString(Snapshot::CommitKind::Analyze()));
ASSERT_EQ(Snapshot::CommitKind::Append(), Snapshot::CommitKind::FromString("APPEND"));
ASSERT_EQ(Snapshot::CommitKind::Compact(), Snapshot::CommitKind::FromString("COMPACT"));
ASSERT_EQ(Snapshot::CommitKind::Overwrite(), Snapshot::CommitKind::FromString("OVERWRITE"));
ASSERT_EQ(Snapshot::CommitKind::Analyze(), Snapshot::CommitKind::FromString("ANALYZE"));
// Verify equality/inequality
ASSERT_FALSE(Snapshot::CommitKind::Analyze() == Snapshot::CommitKind::Append());
ASSERT_FALSE(Snapshot::CommitKind::Analyze() == Snapshot::CommitKind::Compact());
ASSERT_FALSE(Snapshot::CommitKind::Analyze() == Snapshot::CommitKind::Overwrite());
ASSERT_TRUE(Snapshot::CommitKind::Analyze() == Snapshot::CommitKind::Analyze());
}
TEST_F(SnapshotTest, TestSnapshotInfoCommitKindToString) {
ASSERT_EQ("APPEND", SnapshotInfo::CommitKindToString(SnapshotInfo::CommitKind::APPEND));
ASSERT_EQ("COMPACT", SnapshotInfo::CommitKindToString(SnapshotInfo::CommitKind::COMPACT));
ASSERT_EQ("OVERWRITE", SnapshotInfo::CommitKindToString(SnapshotInfo::CommitKind::OVERWRITE));
ASSERT_EQ("ANALYZE", SnapshotInfo::CommitKindToString(SnapshotInfo::CommitKind::ANALYZE));
ASSERT_EQ("UNKNOWN", SnapshotInfo::CommitKindToString(SnapshotInfo::CommitKind::UNKNOWN));
}
TEST_F(SnapshotTest, TestChangelogManifestListSerialization) {
// Test with changelog_manifest_list set to a non-null value
{
std::string json_str = R"({
"version" : 3,
"id" : 1,
"schemaId" : 0,
"baseManifestList" : "base-manifest-list",
"deltaManifestList" : "delta-manifest-list",
"changelogManifestList" : "changelog-manifest-list",
"changelogManifestListSize" : 42,
"commitUser" : "user-01",
"commitIdentifier" : 100,
"commitKind" : "APPEND",
"timeMillis" : 1700000000000,
"logOffsets" : { },
"totalRecordCount" : 10,
"deltaRecordCount" : 5,
"changelogRecordCount" : 3
})";
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromJsonString(json_str));
ASSERT_EQ("changelog-manifest-list", snapshot.ChangelogManifestList().value());
ASSERT_EQ(42, snapshot.ChangelogManifestListSize().value());
ASSERT_OK_AND_ASSIGN(std::string serialized, snapshot.ToJsonString());
ASSERT_EQ(ReplaceAll(json_str), ReplaceAll(serialized));
// Verify round-trip
ASSERT_OK_AND_ASSIGN(Snapshot deserialized, Snapshot::FromJsonString(serialized));
ASSERT_EQ(snapshot, deserialized);
}
// Test with changelog_manifest_list set to null
{
std::string json_str = R"({
"version" : 3,
"id" : 2,
"schemaId" : 0,
"baseManifestList" : "base-manifest-list",
"deltaManifestList" : "delta-manifest-list",
"changelogManifestList" : null,
"commitUser" : "user-02",
"commitIdentifier" : 200,
"commitKind" : "COMPACT",
"timeMillis" : 1700000001000,
"logOffsets" : { },
"totalRecordCount" : 20,
"deltaRecordCount" : 10,
"changelogRecordCount" : 0
})";
ASSERT_OK_AND_ASSIGN(Snapshot snapshot, Snapshot::FromJsonString(json_str));
ASSERT_EQ(std::nullopt, snapshot.ChangelogManifestList());
ASSERT_EQ(std::nullopt, snapshot.ChangelogManifestListSize());
ASSERT_OK_AND_ASSIGN(std::string serialized, snapshot.ToJsonString());
ASSERT_EQ(ReplaceAll(json_str), ReplaceAll(serialized));
// Verify round-trip
ASSERT_OK_AND_ASSIGN(Snapshot deserialized, Snapshot::FromJsonString(serialized));
ASSERT_EQ(snapshot, deserialized);
}
}
} // namespace paimon::test