Skip to content

Commit 36755e3

Browse files
committed
feat: add ExpireSnapshots following 3-goal update pattern
Add ExpireSnapshots as a concrete implementation in iceberg/update/ directory, following the 3-goal architecture for table updates. Goals accomplished: 1. TableMetadataBuilder::RemoveSnapshots() methods exist 2. table::RemoveSnapshots TableUpdate class exists 3. Expose via Table::ExpireSnapshots() and Transaction::ExpireSnapshots() Implementation details: - ExpireSnapshots extends PendingUpdateTyped<vector<shared_ptr<Snapshot>>> - Fluent API with builder methods: ExpireSnapshotId, ExpireOlderThan, RetainLast, DeleteWith, SetCleanupLevel - Table::ExpireSnapshots() factory method creates instances - Transaction::ExpireSnapshots() interface method added - Organized in src/iceberg/update/ subdirectory structure Builder methods: - ExpireSnapshotId: Mark specific snapshots for removal by ID - ExpireOlderThan: Expire snapshots older than timestamp - RetainLast: Keep N most recent snapshots - DeleteWith: Custom file deletion callback - SetCleanupLevel: Control cleanup scope (None/MetadataOnly/All) Apply() and Commit() have placeholder implementations that will be completed when snapshot expiration logic is fully implemented.
1 parent 9805fae commit 36755e3

File tree

12 files changed

+461
-0
lines changed

12 files changed

+461
-0
lines changed

src/iceberg/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ set(ICEBERG_SOURCES
6666
transform.cc
6767
transform_function.cc
6868
type.cc
69+
update/expire_snapshots.cc
6970
util/bucket_util.cc
7071
util/conversions.cc
7172
util/decimal.cc
@@ -132,6 +133,7 @@ iceberg_install_all_headers(iceberg)
132133
add_subdirectory(catalog)
133134
add_subdirectory(expression)
134135
add_subdirectory(row)
136+
add_subdirectory(update)
135137
add_subdirectory(util)
136138

137139
if(ICEBERG_BUILD_BUNDLE)

src/iceberg/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ iceberg_sources = files(
8888
'transform.cc',
8989
'transform_function.cc',
9090
'type.cc',
91+
'update/expire_snapshots.cc',
9192
'util/bucket_util.cc',
9293
'util/conversions.cc',
9394
'util/decimal.cc',
@@ -201,6 +202,7 @@ install_headers(
201202
subdir('catalog')
202203
subdir('expression')
203204
subdir('row')
205+
subdir('update')
204206
subdir('util')
205207

206208
if get_option('tests').enabled()

src/iceberg/table.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "iceberg/table_metadata.h"
2929
#include "iceberg/table_properties.h"
3030
#include "iceberg/table_scan.h"
31+
#include "iceberg/update/expire_snapshots.h"
3132
#include "iceberg/util/macros.h"
3233

3334
namespace iceberg {
@@ -114,6 +115,10 @@ std::unique_ptr<Transaction> Table::NewTransaction() const {
114115
throw NotImplemented("Table::NewTransaction is not implemented");
115116
}
116117

118+
std::shared_ptr<iceberg::ExpireSnapshots> Table::NewExpireSnapshots() {
119+
return std::make_shared<iceberg::ExpireSnapshots>(this);
120+
}
121+
117122
const std::shared_ptr<FileIO>& Table::io() const { return io_; }
118123

119124
std::unique_ptr<TableScanBuilder> Table::NewScan() const {

src/iceberg/table.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ class ICEBERG_EXPORT Table {
115115
/// \return a pointer to the new Transaction
116116
virtual std::unique_ptr<Transaction> NewTransaction() const;
117117

118+
/// \brief Create a new expire snapshots operation for this table
119+
///
120+
/// \return a shared pointer to the new ExpireSnapshots operation
121+
virtual std::shared_ptr<ExpireSnapshots> NewExpireSnapshots();
122+
118123
/// \brief Returns a FileIO to read and write table data and metadata files
119124
const std::shared_ptr<FileIO>& io() const;
120125

src/iceberg/test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ add_iceberg_test(expression_test
9797
literal_test.cc
9898
predicate_test.cc)
9999

100+
add_iceberg_test(update_test SOURCES update/expire_snapshots_test.cc)
101+
100102
add_iceberg_test(json_serde_test
101103
SOURCES
102104
test_common.cc
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/update/expire_snapshots.h"
21+
22+
#include <memory>
23+
#include <vector>
24+
25+
#include <gtest/gtest.h>
26+
27+
#include "iceberg/result.h"
28+
#include "iceberg/snapshot.h"
29+
#include "iceberg/test/matchers.h"
30+
31+
namespace iceberg {
32+
33+
// Basic API tests for ExpireSnapshots
34+
// Full functional tests will be added when the implementation is complete
35+
36+
TEST(ExpireSnapshotsTest, FluentApiChaining) {
37+
// Test that the fluent API works correctly with method chaining
38+
ExpireSnapshots expire(nullptr);
39+
40+
auto& result =
41+
expire.ExpireSnapshotId(123).ExpireOlderThan(1000000).RetainLast(5).SetCleanupLevel(
42+
CleanupLevel::kMetadataOnly);
43+
44+
// Verify that chaining returns the same object
45+
EXPECT_EQ(&result, &expire);
46+
}
47+
48+
TEST(ExpireSnapshotsTest, ExpireSnapshotId) {
49+
ExpireSnapshots expire(nullptr);
50+
expire.ExpireSnapshotId(123);
51+
52+
// Currently returns NotImplemented - this test will be expanded
53+
// when the actual implementation is added
54+
auto result = expire.Apply();
55+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
56+
}
57+
58+
TEST(ExpireSnapshotsTest, ExpireMultipleSnapshotIds) {
59+
ExpireSnapshots expire(nullptr);
60+
expire.ExpireSnapshotId(100).ExpireSnapshotId(200).ExpireSnapshotId(300);
61+
62+
// Currently returns NotImplemented
63+
auto result = expire.Apply();
64+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
65+
}
66+
67+
TEST(ExpireSnapshotsTest, ExpireOlderThan) {
68+
ExpireSnapshots expire(nullptr);
69+
int64_t timestamp = 1609459200000; // 2021-01-01 00:00:00 UTC
70+
expire.ExpireOlderThan(timestamp);
71+
72+
// Currently returns NotImplemented
73+
auto result = expire.Apply();
74+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
75+
}
76+
77+
TEST(ExpireSnapshotsTest, RetainLast) {
78+
ExpireSnapshots expire(nullptr);
79+
expire.RetainLast(10);
80+
81+
// Currently returns NotImplemented
82+
auto result = expire.Apply();
83+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
84+
}
85+
86+
TEST(ExpireSnapshotsTest, DeleteWithCallback) {
87+
ExpireSnapshots expire(nullptr);
88+
std::vector<std::string> deleted_files;
89+
90+
expire.DeleteWith(
91+
[&deleted_files](std::string_view file) { deleted_files.emplace_back(file); });
92+
93+
// Currently returns NotImplemented
94+
auto result = expire.Commit();
95+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
96+
}
97+
98+
TEST(ExpireSnapshotsTest, CleanupLevelNone) {
99+
ExpireSnapshots expire(nullptr);
100+
expire.SetCleanupLevel(CleanupLevel::kNone);
101+
102+
// Currently returns NotImplemented
103+
auto result = expire.Apply();
104+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
105+
}
106+
107+
TEST(ExpireSnapshotsTest, CleanupLevelMetadataOnly) {
108+
ExpireSnapshots expire(nullptr);
109+
expire.SetCleanupLevel(CleanupLevel::kMetadataOnly);
110+
111+
// Currently returns NotImplemented
112+
auto result = expire.Apply();
113+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
114+
}
115+
116+
TEST(ExpireSnapshotsTest, CleanupLevelAll) {
117+
ExpireSnapshots expire(nullptr);
118+
expire.SetCleanupLevel(CleanupLevel::kAll);
119+
120+
// Currently returns NotImplemented
121+
auto result = expire.Apply();
122+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
123+
}
124+
125+
TEST(ExpireSnapshotsTest, CombinedConfiguration) {
126+
ExpireSnapshots expire(nullptr);
127+
int64_t timestamp = 1609459200000;
128+
129+
expire.ExpireSnapshotId(100)
130+
.ExpireSnapshotId(200)
131+
.ExpireOlderThan(timestamp)
132+
.RetainLast(5)
133+
.SetCleanupLevel(CleanupLevel::kMetadataOnly);
134+
135+
// Currently returns NotImplemented
136+
auto result = expire.Apply();
137+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
138+
}
139+
140+
TEST(ExpireSnapshotsTest, CommitNotImplemented) {
141+
ExpireSnapshots expire(nullptr);
142+
expire.ExpireSnapshotId(123);
143+
144+
// Currently returns NotImplemented
145+
auto result = expire.Commit();
146+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
147+
}
148+
149+
TEST(ExpireSnapshotsTest, ApplyNotImplemented) {
150+
ExpireSnapshots expire(nullptr);
151+
expire.ExpireOlderThan(1000000);
152+
153+
// Currently returns NotImplemented
154+
auto result = expire.Apply();
155+
EXPECT_THAT(result, IsError(ErrorKind::kNotImplemented));
156+
}
157+
158+
} // namespace iceberg

src/iceberg/transaction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ class ICEBERG_EXPORT Transaction {
4444
/// \return a new AppendFiles
4545
virtual std::shared_ptr<AppendFiles> NewAppend() = 0;
4646

47+
/// \brief Create a new expire snapshots operation for this transaction
48+
///
49+
/// \return a shared pointer to the new ExpireSnapshots operation
50+
virtual std::shared_ptr<ExpireSnapshots> NewExpireSnapshots() = 0;
51+
4752
/// \brief Apply the pending changes from all actions and commit
4853
///
4954
/// This method applies all pending data operations and metadata updates in the

src/iceberg/type_fwd.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class PendingUpdate;
161161
template <typename T>
162162
class PendingUpdateTyped;
163163

164+
enum class CleanupLevel;
165+
class ExpireSnapshots;
166+
164167
/// ----------------------------------------------------------------------------
165168
/// TODO: Forward declarations below are not added yet.
166169
/// ----------------------------------------------------------------------------

src/iceberg/update/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
iceberg_install_all_headers(iceberg/update)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/update/expire_snapshots.h"
21+
22+
#include "iceberg/result.h"
23+
#include "iceberg/snapshot.h"
24+
#include "iceberg/table.h"
25+
#include "iceberg/table_metadata.h"
26+
27+
namespace iceberg {
28+
29+
ExpireSnapshots::ExpireSnapshots(Table* table) : table_(table) {}
30+
31+
ExpireSnapshots& ExpireSnapshots::ExpireSnapshotId(int64_t snapshot_id) {
32+
snapshot_ids_to_expire_.push_back(snapshot_id);
33+
return *this;
34+
}
35+
36+
ExpireSnapshots& ExpireSnapshots::ExpireOlderThan(int64_t timestamp_millis) {
37+
expire_older_than_ms_ = timestamp_millis;
38+
return *this;
39+
}
40+
41+
ExpireSnapshots& ExpireSnapshots::RetainLast(int num_snapshots) {
42+
retain_last_ = num_snapshots;
43+
return *this;
44+
}
45+
46+
ExpireSnapshots& ExpireSnapshots::DeleteWith(
47+
std::function<void(std::string_view)> delete_func) {
48+
delete_func_ = std::move(delete_func);
49+
return *this;
50+
}
51+
52+
ExpireSnapshots& ExpireSnapshots::SetCleanupLevel(CleanupLevel level) {
53+
cleanup_level_ = level;
54+
return *this;
55+
}
56+
57+
Result<std::vector<std::shared_ptr<Snapshot>>> ExpireSnapshots::Apply() {
58+
// Placeholder implementation - full snapshot expiration logic to be implemented
59+
return NotImplemented("ExpireSnapshots::Apply() is not yet implemented");
60+
}
61+
62+
Status ExpireSnapshots::Commit() {
63+
// Placeholder implementation - full commit logic to be implemented
64+
return NotImplemented("ExpireSnapshots::Commit() is not yet implemented");
65+
}
66+
67+
} // namespace iceberg

0 commit comments

Comments
 (0)