From 9660860736ef1ac11370340d3e454795670f28b9 Mon Sep 17 00:00:00 2001 From: Hongyue Zhang Date: Fri, 9 Oct 2026 15:25:29 -0700 Subject: [PATCH 1/3] API: Define RewrapEncryptionKeys action interface --- .../iceberg/actions/ActionsProvider.java | 6 +++ .../iceberg/actions/RewrapEncryptionKeys.java | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java diff --git a/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java b/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java index 3bb18d8989af..55a668d11312 100644 --- a/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java +++ b/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java @@ -100,4 +100,10 @@ default RepairTable repairTable(Table table) { throw new UnsupportedOperationException( this.getClass().getName() + " does not implement repairTable"); } + + /** Instantiates an action to re-wrap the encryption keys of a table. */ + default RewrapEncryptionKeys rewrapEncryptionKeys(Table table) { + throw new UnsupportedOperationException( + this.getClass().getName() + " does not implement rewrapEncryptionKeys"); + } } diff --git a/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java b/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java new file mode 100644 index 000000000000..e9c654809df8 --- /dev/null +++ b/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java @@ -0,0 +1,38 @@ +/* + * 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. + */ +package org.apache.iceberg.actions; + +import org.apache.iceberg.ManifestFile; + +/** + * An action that re-wraps the encryption keys of a table so that they no longer depend on a + * superseded version of the table master key. + * + *

Only the metadata reachable from the current snapshot is re-wrapped. Data and delete files are + * not rewritten, and older snapshots keep depending on the previous keys until they are expired. + */ +public interface RewrapEncryptionKeys + extends SnapshotUpdate { + + /** The action result that contains a summary of the execution. */ + interface Result { + /** Returns the manifests written with new keys. */ + Iterable rewrappedManifests(); + } +} From c421bf99dea38c78dac833e2ebe383ef40a63bf1 Mon Sep 17 00:00:00 2001 From: Hongyue Zhang Date: Tue, 13 Oct 2026 13:28:45 -0700 Subject: [PATCH 2/3] API: Add a configurable key regeneration level to RewrapEncryptionKeys Re-wrapping every key in a table means rewriting every manifest, which is cost prohibitive for large iceberg tables. The level names the lowest key to regenerate: the keys above it are regenerated too, the keys directly below it are re-wrapped under the new ones, and the keys further below are left unchanged. Generated-by: Claude Code --- .../apache/iceberg/RewrapEncryptionKeys.java | 56 +++++++++++++++++++ .../iceberg/actions/RewrapEncryptionKeys.java | 16 ++++-- 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java diff --git a/api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java b/api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java new file mode 100644 index 000000000000..97141f109f62 --- /dev/null +++ b/api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java @@ -0,0 +1,56 @@ +/* + * 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. + */ +package org.apache.iceberg; + +import java.util.List; + +/** + * API for re-wrapping the encryption keys of a table so that they no longer depend on a superseded + * version of the table master key. + * + *

Only the files reachable from the current snapshot are re-wrapped. Older snapshots keep + * depending on the previous keys until they are expired. + * + *

{@link #apply()} returns the manifests that will be replaced. + */ +public interface RewrapEncryptionKeys extends PendingUpdate> { + + /** + * An enum representing the lowest key that is regenerated. The keys above it are regenerated too, + * the keys directly below it are re-wrapped under the new ones, and the keys further below are + * left unchanged. + */ + enum KeyRegenerationLevel { + /** Regenerate the key encryption key, rewriting no manifest or data file. */ + KEY_ENCRYPTION_KEY, + /** Regenerate the manifest keys and the keys above them, rewriting every manifest (default). */ + MANIFEST_KEY + } + + /** + * Sets the lowest key to regenerate. + * + *

Consider {@link KeyRegenerationLevel#KEY_ENCRYPTION_KEY} when rewriting every manifest is + * too expensive. + * + * @param level the lowest key to regenerate + * @return this for method chaining + */ + RewrapEncryptionKeys keyRegenerationLevel(KeyRegenerationLevel level); +} diff --git a/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java b/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java index e9c654809df8..6cdf25ceab7a 100644 --- a/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java +++ b/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java @@ -19,17 +19,25 @@ package org.apache.iceberg.actions; import org.apache.iceberg.ManifestFile; +import org.apache.iceberg.RewrapEncryptionKeys.KeyRegenerationLevel; /** - * An action that re-wraps the encryption keys of a table so that they no longer depend on a - * superseded version of the table master key. + * An action that re-wraps the encryption keys of a table. * - *

Only the metadata reachable from the current snapshot is re-wrapped. Data and delete files are - * not rewritten, and older snapshots keep depending on the previous keys until they are expired. + *

Similar to {@link org.apache.iceberg.RewrapEncryptionKeys} but may use a query engine to + * distribute parts of the work. */ public interface RewrapEncryptionKeys extends SnapshotUpdate { + /** + * Sets the lowest key to regenerate. + * + * @param level the lowest key to regenerate + * @return this for method chaining + */ + RewrapEncryptionKeys keyRegenerationLevel(KeyRegenerationLevel level); + /** The action result that contains a summary of the execution. */ interface Result { /** Returns the manifests written with new keys. */ From b2a6febe65f59146bb49b6caa1509c8535ff7591 Mon Sep 17 00:00:00 2001 From: Hongyue Zhang Date: Wed, 14 Oct 2026 17:02:32 -0700 Subject: [PATCH 3/3] API: Rename RewrapEncryptionKeys to RotateEncryptionKeys Also add SNAPSHOT_KEY level to the enum --- ...ionKeys.java => RotateEncryptionKeys.java} | 21 ++++++++++++------- .../iceberg/actions/ActionsProvider.java | 6 +++--- ...ionKeys.java => RotateEncryptionKeys.java} | 14 ++++++------- 3 files changed, 23 insertions(+), 18 deletions(-) rename api/src/main/java/org/apache/iceberg/{RewrapEncryptionKeys.java => RotateEncryptionKeys.java} (69%) rename api/src/main/java/org/apache/iceberg/actions/{RewrapEncryptionKeys.java => RotateEncryptionKeys.java} (76%) diff --git a/api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java b/api/src/main/java/org/apache/iceberg/RotateEncryptionKeys.java similarity index 69% rename from api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java rename to api/src/main/java/org/apache/iceberg/RotateEncryptionKeys.java index 97141f109f62..567670b79ebc 100644 --- a/api/src/main/java/org/apache/iceberg/RewrapEncryptionKeys.java +++ b/api/src/main/java/org/apache/iceberg/RotateEncryptionKeys.java @@ -21,15 +21,15 @@ import java.util.List; /** - * API for re-wrapping the encryption keys of a table so that they no longer depend on a superseded + * API for rotating the encryption keys of a table so that they no longer depend on a superseded * version of the table master key. * - *

Only the files reachable from the current snapshot are re-wrapped. Older snapshots keep - * depending on the previous keys until they are expired. + *

Only the keys reachable from the current snapshot are rotated. Older snapshots keep depending + * on the previous keys until they are expired. * *

{@link #apply()} returns the manifests that will be replaced. */ -public interface RewrapEncryptionKeys extends PendingUpdate> { +public interface RotateEncryptionKeys extends PendingUpdate> { /** * An enum representing the lowest key that is regenerated. The keys above it are regenerated too, @@ -37,8 +37,13 @@ public interface RewrapEncryptionKeys extends PendingUpdate> * left unchanged. */ enum KeyRegenerationLevel { - /** Regenerate the key encryption key, rewriting no manifest or data file. */ + /** Regenerate the key encryption key without committing a new snapshot. */ KEY_ENCRYPTION_KEY, + /** + * Regenerate the manifest list key and the keys above it, committing a new snapshot without + * rewriting any manifest. + */ + SNAPSHOT_KEY, /** Regenerate the manifest keys and the keys above them, rewriting every manifest (default). */ MANIFEST_KEY } @@ -46,11 +51,11 @@ enum KeyRegenerationLevel { /** * Sets the lowest key to regenerate. * - *

Consider {@link KeyRegenerationLevel#KEY_ENCRYPTION_KEY} when rewriting every manifest is - * too expensive. + *

Consider {@link KeyRegenerationLevel#KEY_ENCRYPTION_KEY} or {@link + * KeyRegenerationLevel#SNAPSHOT_KEY} when rewriting every manifest is too expensive. * * @param level the lowest key to regenerate * @return this for method chaining */ - RewrapEncryptionKeys keyRegenerationLevel(KeyRegenerationLevel level); + RotateEncryptionKeys keyRegenerationLevel(KeyRegenerationLevel level); } diff --git a/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java b/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java index 55a668d11312..12e1a79e1093 100644 --- a/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java +++ b/api/src/main/java/org/apache/iceberg/actions/ActionsProvider.java @@ -101,9 +101,9 @@ default RepairTable repairTable(Table table) { this.getClass().getName() + " does not implement repairTable"); } - /** Instantiates an action to re-wrap the encryption keys of a table. */ - default RewrapEncryptionKeys rewrapEncryptionKeys(Table table) { + /** Instantiates an action to rotate the encryption keys of a table. */ + default RotateEncryptionKeys rotateEncryptionKeys(Table table) { throw new UnsupportedOperationException( - this.getClass().getName() + " does not implement rewrapEncryptionKeys"); + this.getClass().getName() + " does not implement rotateEncryptionKeys"); } } diff --git a/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java b/api/src/main/java/org/apache/iceberg/actions/RotateEncryptionKeys.java similarity index 76% rename from api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java rename to api/src/main/java/org/apache/iceberg/actions/RotateEncryptionKeys.java index 6cdf25ceab7a..6085fdc7cc2f 100644 --- a/api/src/main/java/org/apache/iceberg/actions/RewrapEncryptionKeys.java +++ b/api/src/main/java/org/apache/iceberg/actions/RotateEncryptionKeys.java @@ -19,16 +19,16 @@ package org.apache.iceberg.actions; import org.apache.iceberg.ManifestFile; -import org.apache.iceberg.RewrapEncryptionKeys.KeyRegenerationLevel; +import org.apache.iceberg.RotateEncryptionKeys.KeyRegenerationLevel; /** - * An action that re-wraps the encryption keys of a table. + * An action that rotates the encryption keys of a table. * - *

Similar to {@link org.apache.iceberg.RewrapEncryptionKeys} but may use a query engine to + *

Similar to {@link org.apache.iceberg.RotateEncryptionKeys} but may use a query engine to * distribute parts of the work. */ -public interface RewrapEncryptionKeys - extends SnapshotUpdate { +public interface RotateEncryptionKeys + extends SnapshotUpdate { /** * Sets the lowest key to regenerate. @@ -36,11 +36,11 @@ public interface RewrapEncryptionKeys * @param level the lowest key to regenerate * @return this for method chaining */ - RewrapEncryptionKeys keyRegenerationLevel(KeyRegenerationLevel level); + RotateEncryptionKeys keyRegenerationLevel(KeyRegenerationLevel level); /** The action result that contains a summary of the execution. */ interface Result { /** Returns the manifests written with new keys. */ - Iterable rewrappedManifests(); + Iterable addedManifests(); } }