2020#include " iceberg/update/fast_append.h"
2121
2222#include < format>
23+ #include < optional>
24+ #include < string>
25+ #include < vector>
2326
2427#include < gmock/gmock.h>
2528#include < gtest/gtest.h>
2629
2730#include " iceberg/avro/avro_register.h"
31+ #include " iceberg/constants.h"
32+ #include " iceberg/manifest/manifest_entry.h"
33+ #include " iceberg/manifest/manifest_writer.h"
2834#include " iceberg/partition_spec.h"
2935#include " iceberg/schema.h"
36+ #include " iceberg/snapshot.h"
3037#include " iceberg/table_metadata.h"
3138#include " iceberg/test/matchers.h"
3239#include " iceberg/test/test_resource.h"
@@ -72,6 +79,23 @@ class FastAppendTest : public UpdateTestBase {
7279 return data_file;
7380 }
7481
82+ Result<ManifestFile> WriteManifest (
83+ const std::string& path, const std::vector<std::shared_ptr<DataFile>>& files) {
84+ ICEBERG_ASSIGN_OR_RAISE (
85+ auto writer, ManifestWriter::MakeWriter (table_->metadata ()->format_version ,
86+ kInvalidSnapshotId , path, file_io_, spec_,
87+ schema_, ManifestContent::kData ));
88+ for (const auto & file : files) {
89+ ManifestEntry entry;
90+ entry.status = ManifestStatus::kAdded ;
91+ entry.snapshot_id = std::nullopt ;
92+ entry.data_file = file;
93+ ICEBERG_RETURN_UNEXPECTED (writer->WriteAddedEntry (entry));
94+ }
95+ ICEBERG_RETURN_UNEXPECTED (writer->Close ());
96+ return writer->ToManifestFile ();
97+ }
98+
7599 std::shared_ptr<PartitionSpec> spec_;
76100 std::shared_ptr<Schema> schema_;
77101 std::shared_ptr<DataFile> file_a_;
@@ -90,6 +114,9 @@ TEST_F(FastAppendTest, AppendDataFile) {
90114 EXPECT_EQ (snapshot->summary .at (" added-data-files" ), " 1" );
91115 EXPECT_EQ (snapshot->summary .at (" added-records" ), " 100" );
92116 EXPECT_EQ (snapshot->summary .at (" added-files-size" ), " 1024" );
117+ EXPECT_EQ (snapshot->summary .at (SnapshotSummaryFields::kManifestsCreated ), " 1" );
118+ EXPECT_EQ (snapshot->summary .at (SnapshotSummaryFields::kManifestsKept ), " 0" );
119+ EXPECT_EQ (snapshot->summary .at (SnapshotSummaryFields::kManifestsReplaced ), " 0" );
93120}
94121
95122TEST_F (FastAppendTest, AppendMultipleDataFiles) {
@@ -172,6 +199,40 @@ TEST_F(FastAppendTest, FinalizeIgnoresCleanupDeleteFailure) {
172199 IsOk ());
173200}
174201
202+ TEST_F (FastAppendTest, RetryCopiesAppendManifestAgain) {
203+ table_->metadata ()->format_version = 1 ;
204+ const auto path = table_location_ + " /metadata/input.avro" ;
205+ ICEBERG_UNWRAP_OR_FAIL (auto manifest, WriteManifest (path, {file_a_}));
206+
207+ std::shared_ptr<FastAppend> fast_append;
208+ ICEBERG_UNWRAP_OR_FAIL (fast_append, table_->NewFastAppend ());
209+ std::vector<std::string> deleted_paths;
210+ fast_append->DeleteWith ([&](const std::string& deleted_path) {
211+ deleted_paths.push_back (deleted_path);
212+ return file_io_->DeleteFile (deleted_path);
213+ });
214+ fast_append->AppendManifest (manifest);
215+
216+ auto & update = static_cast <SnapshotUpdate&>(*fast_append);
217+ // First Apply() copies the input manifest because v1 cannot inherit snapshot IDs.
218+ ICEBERG_UNWRAP_OR_FAIL (auto first_apply, update.Apply ());
219+ SnapshotCache first_cache (first_apply.snapshot .get ());
220+ ICEBERG_UNWRAP_OR_FAIL (auto first_manifests, first_cache.Manifests (file_io_));
221+ ASSERT_EQ (first_manifests.size (), 1U );
222+ const auto first_rewritten_path = first_manifests[0 ].manifest_path ;
223+ EXPECT_NE (first_rewritten_path, path);
224+
225+ // Second Apply() simulates retry cleanup, then copies the original manifest again.
226+ ICEBERG_UNWRAP_OR_FAIL (auto second_apply, update.Apply ());
227+ EXPECT_THAT (deleted_paths, testing::Contains (first_rewritten_path));
228+
229+ SnapshotCache second_cache (second_apply.snapshot .get ());
230+ ICEBERG_UNWRAP_OR_FAIL (auto second_manifests, second_cache.Manifests (file_io_));
231+ ASSERT_EQ (second_manifests.size (), 1U );
232+ EXPECT_NE (second_manifests[0 ].manifest_path , path);
233+ EXPECT_NE (second_manifests[0 ].manifest_path , first_rewritten_path);
234+ }
235+
175236TEST_F (FastAppendTest, AppendDuplicateFile) {
176237 std::shared_ptr<FastAppend> fast_append;
177238 ICEBERG_UNWRAP_OR_FAIL (fast_append, table_->NewFastAppend ());
0 commit comments