Skip to content

Commit 6d4680a

Browse files
committed
address minor issues
1 parent 8708230 commit 6d4680a

6 files changed

Lines changed: 101 additions & 19 deletions

File tree

src/iceberg/test/fast_append_test.cc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,20 @@
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

95122
TEST_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+
175236
TEST_F(FastAppendTest, AppendDuplicateFile) {
176237
std::shared_ptr<FastAppend> fast_append;
177238
ICEBERG_UNWRAP_OR_FAIL(fast_append, table_->NewFastAppend());

src/iceberg/update/fast_append.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ Result<std::vector<ManifestFile>> FastAppend::Apply(
9595
std::vector<ManifestFile> manifests;
9696

9797
ICEBERG_ASSIGN_OR_RAISE(auto new_written_manifests, WriteNewManifests());
98+
// A retry cleanup deletes copied append manifests and clears the rewritten
99+
// list; rebuild them from the original appended manifests before re-applying.
98100
if (rewritten_append_manifests_.empty() && !append_manifests_to_copy_.empty()) {
99101
for (const auto& manifest : append_manifests_to_copy_) {
100102
ICEBERG_ASSIGN_OR_RAISE(auto copied_manifest,
@@ -132,6 +134,9 @@ Result<std::vector<ManifestFile>> FastAppend::Apply(
132134
snapshot_manifests.end());
133135
}
134136

137+
manifest_count_summary_ =
138+
BuildManifestCountSummary(manifests, /*replaced_manifests_count=*/0);
139+
135140
return manifests;
136141
}
137142

@@ -144,6 +149,7 @@ std::unordered_map<std::string, std::string> FastAppend::Summary() {
144149
for (const auto& [property, value] : custom_summary_properties_) {
145150
summary_.Set(property, value);
146151
}
152+
summary_.Merge(manifest_count_summary_);
147153
return summary_.Build();
148154
}
149155

@@ -179,9 +185,9 @@ Status FastAppend::CleanUncommitted(const std::unordered_set<std::string>& commi
179185
}
180186

181187
bool FastAppend::CleanupAfterCommit() const {
182-
// Cleanup after committing is disabled for FastAppend unless there are
183-
// rewritten_append_manifests_ because:
184-
// 1.) Appended manifests are never rewritten
188+
// Cleanup after committing is disabled for FastAppend unless append manifests
189+
// were copied or need to be copied on retry because:
190+
// 1.) Directly appended manifests are never rewritten
185191
// 2.) Manifests which are written out as part of AppendFile are already cleaned
186192
// up between commit attempts in WriteNewManifests
187193
return !rewritten_append_manifests_.empty() || !append_manifests_to_copy_.empty();

src/iceberg/update/fast_append.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class ICEBERG_EXPORT FastAppend : public SnapshotUpdate {
8585
/// \brief Copy a manifest file with a new snapshot ID.
8686
///
8787
/// \param manifest The manifest to copy
88+
/// \param update_summary Whether to add copied entries to the append summary
8889
/// \return The copied manifest file
8990
Result<ManifestFile> CopyManifest(const ManifestFile& manifest, bool update_summary);
9091

@@ -96,13 +97,18 @@ class ICEBERG_EXPORT FastAppend : public SnapshotUpdate {
9697
private:
9798
std::string table_name_;
9899
std::unordered_map<int32_t, DataFileSet> new_data_files_by_spec_;
100+
// Stable input summaries for retry-safe summary_ rebuilds.
99101
SnapshotSummaryBuilder added_data_files_summary_;
100102
SnapshotSummaryBuilder appended_manifests_summary_;
103+
// User-provided summary properties restored after summary_ rebuilds.
101104
std::unordered_map<std::string, std::string> custom_summary_properties_;
102105
std::vector<ManifestFile> append_manifests_;
106+
// Original manifests kept to recreate copied manifests after retry cleanup.
103107
std::vector<ManifestFile> append_manifests_to_copy_;
104108
std::vector<ManifestFile> rewritten_append_manifests_;
105109
std::vector<ManifestFile> new_manifests_;
110+
// Manifest count summary from the latest Apply() result.
111+
SnapshotSummaryBuilder manifest_count_summary_;
106112
bool has_new_files_{false};
107113
};
108114

src/iceberg/update/merging_snapshot_update.cc

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -944,26 +944,11 @@ Result<std::vector<ManifestFile>> MergingSnapshotUpdate::Apply(
944944
result.insert(result.end(), std::make_move_iterator(merged_deletes.begin()),
945945
std::make_move_iterator(merged_deletes.end()));
946946

947-
// Manifest count summary: unassigned manifests count as neither created nor kept.
948-
int32_t manifests_created = 0;
949-
int32_t manifests_kept = 0;
950-
for (const auto& m : result) {
951-
if (m.added_snapshot_id == snapshot_id) {
952-
++manifests_created;
953-
} else if (m.added_snapshot_id != kInvalidSnapshotId) {
954-
++manifests_kept;
955-
}
956-
}
957947
int32_t replaced_manifests_count = data_filter_manager_->ReplacedManifestsCount() +
958948
delete_filter_manager_->ReplacedManifestsCount() +
959949
data_merge_manager_->ReplacedManifestsCount() +
960950
delete_merge_manager_->ReplacedManifestsCount();
961-
summary_builder().Set(SnapshotSummaryFields::kManifestsCreated,
962-
std::to_string(manifests_created));
963-
summary_builder().Set(SnapshotSummaryFields::kManifestsKept,
964-
std::to_string(manifests_kept));
965-
summary_builder().Set(SnapshotSummaryFields::kManifestsReplaced,
966-
std::to_string(replaced_manifests_count));
951+
summary_builder().Merge(BuildManifestCountSummary(result, replaced_manifests_count));
967952

968953
return result;
969954
}

src/iceberg/update/snapshot_update.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,28 @@ std::string SnapshotUpdate::ManifestListPath() {
424424
return ctx_->MetadataFileLocation(filename);
425425
}
426426

427+
SnapshotSummaryBuilder SnapshotUpdate::BuildManifestCountSummary(
428+
std::span<const ManifestFile> manifests, int32_t replaced_manifests_count) {
429+
SnapshotSummaryBuilder summary;
430+
int32_t manifests_created = 0;
431+
int32_t manifests_kept = 0;
432+
int64_t snapshot_id = SnapshotId();
433+
for (const auto& manifest : manifests) {
434+
if (manifest.added_snapshot_id == snapshot_id) {
435+
++manifests_created;
436+
} else if (manifest.added_snapshot_id != kInvalidSnapshotId) {
437+
++manifests_kept;
438+
}
439+
}
440+
441+
summary.Set(SnapshotSummaryFields::kManifestsCreated,
442+
std::to_string(manifests_created));
443+
summary.Set(SnapshotSummaryFields::kManifestsKept, std::to_string(manifests_kept));
444+
summary.Set(SnapshotSummaryFields::kManifestsReplaced,
445+
std::to_string(replaced_manifests_count));
446+
return summary;
447+
}
448+
427449
std::string SnapshotUpdate::ManifestPath() {
428450
// Generate manifest path
429451
// Format: {metadata_location}/{uuid}-m{manifest_count}.avro

src/iceberg/update/snapshot_update.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ class ICEBERG_EXPORT SnapshotUpdate : public PendingUpdate {
216216
std::string ManifestPath();
217217
std::string ManifestListPath();
218218
SnapshotSummaryBuilder& summary_builder() { return summary_; }
219+
SnapshotSummaryBuilder BuildManifestCountSummary(
220+
std::span<const ManifestFile> manifests, int32_t replaced_manifests_count);
219221

220222
private:
221223
/// \brief Returns the snapshot summary from the implementation and updates totals.

0 commit comments

Comments
 (0)