This is an automated email from the ASF dual-hosted git repository.

etudenhoefner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/main by this push:
     new 42a2c19cec Core: Only write view history when currentVersionId changes 
(#9725)
42a2c19cec is described below

commit 42a2c19cec31c626cbff6cc2dfafb86cdf223bd0
Author: Eduard Tudenhoefner <[email protected]>
AuthorDate: Thu Feb 15 11:19:28 2024 +0100

    Core: Only write view history when currentVersionId changes (#9725)
---
 .../java/org/apache/iceberg/view/ViewMetadata.java |  20 ++-
 .../rest/responses/TestLoadViewResponseParser.java |  12 --
 .../org/apache/iceberg/view/TestViewMetadata.java  | 136 +++++++++++++++++----
 .../iceberg/view/TestViewMetadataParser.java       |  48 +++++---
 4 files changed, 152 insertions(+), 64 deletions(-)

diff --git a/core/src/main/java/org/apache/iceberg/view/ViewMetadata.java 
b/core/src/main/java/org/apache/iceberg/view/ViewMetadata.java
index fa75c352f1..68d8c64587 100644
--- a/core/src/main/java/org/apache/iceberg/view/ViewMetadata.java
+++ b/core/src/main/java/org/apache/iceberg/view/ViewMetadata.java
@@ -31,6 +31,7 @@ import javax.annotation.Nullable;
 import org.apache.iceberg.MetadataUpdate;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.exceptions.ValidationException;
+import 
org.apache.iceberg.relocated.com.google.common.annotations.VisibleForTesting;
 import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
 import org.apache.iceberg.relocated.com.google.common.collect.Lists;
@@ -156,6 +157,7 @@ public interface ViewMetadata extends Serializable {
     // internal change tracking
     private Integer lastAddedVersionId = null;
     private Integer lastAddedSchemaId = null;
+    private ViewHistoryEntry historyEntry = null;
 
     // indexes
     private final Map<Integer, ViewVersion> versionsById;
@@ -243,6 +245,12 @@ public interface ViewMetadata extends Serializable {
         changes.add(new MetadataUpdate.SetCurrentViewVersion(newVersionId));
       }
 
+      this.historyEntry =
+          ImmutableViewHistoryEntry.builder()
+              .timestampMillis(version.timestampMillis())
+              .versionId(version.versionId())
+              .build();
+
       return this;
     }
 
@@ -307,12 +315,6 @@ public interface ViewMetadata extends Serializable {
         changes.add(new MetadataUpdate.AddViewVersion(version));
       }
 
-      history.add(
-          ImmutableViewHistoryEntry.builder()
-              .timestampMillis(version.timestampMillis())
-              .versionId(version.versionId())
-              .build());
-
       this.lastAddedVersionId = newVersionId;
 
       return newVersionId;
@@ -438,6 +440,10 @@ public interface ViewMetadata extends Serializable {
           metadataLocation == null || changes.isEmpty(),
           "Cannot create view metadata with a metadata location and changes");
 
+      if (null != historyEntry) {
+        history.add(historyEntry);
+      }
+
       int historySize =
           PropertyUtil.propertyAsInt(
               properties,
@@ -479,6 +485,7 @@ public interface ViewMetadata extends Serializable {
           metadataLocation);
     }
 
+    @VisibleForTesting
     static List<ViewVersion> expireVersions(
         Map<Integer, ViewVersion> versionsById, int numVersionsToKeep) {
       // version ids are assigned sequentially. keep the latest versions by ID.
@@ -493,6 +500,7 @@ public interface ViewMetadata extends Serializable {
       return retainedVersions;
     }
 
+    @VisibleForTesting
     static List<ViewHistoryEntry> updateHistory(List<ViewHistoryEntry> 
history, Set<Integer> ids) {
       List<ViewHistoryEntry> retainedHistory = Lists.newArrayList();
       for (ViewHistoryEntry entry : history) {
diff --git 
a/core/src/test/java/org/apache/iceberg/rest/responses/TestLoadViewResponseParser.java
 
b/core/src/test/java/org/apache/iceberg/rest/responses/TestLoadViewResponseParser.java
index d94d035596..f3de08cd29 100644
--- 
a/core/src/test/java/org/apache/iceberg/rest/responses/TestLoadViewResponseParser.java
+++ 
b/core/src/test/java/org/apache/iceberg/rest/responses/TestLoadViewResponseParser.java
@@ -137,12 +137,6 @@ public class TestLoadViewResponseParser {
             + "      \"representations\" : [ ]\n"
             + "    } ],\n"
             + "    \"version-log\" : [ {\n"
-            + "      \"timestamp-ms\" : 23,\n"
-            + "      \"version-id\" : 1\n"
-            + "    }, {\n"
-            + "      \"timestamp-ms\" : 24,\n"
-            + "      \"version-id\" : 2\n"
-            + "    }, {\n"
             + "      \"timestamp-ms\" : 25,\n"
             + "      \"version-id\" : 3\n"
             + "    } ]\n"
@@ -235,12 +229,6 @@ public class TestLoadViewResponseParser {
             + "      \"representations\" : [ ]\n"
             + "    } ],\n"
             + "    \"version-log\" : [ {\n"
-            + "      \"timestamp-ms\" : 23,\n"
-            + "      \"version-id\" : 1\n"
-            + "    }, {\n"
-            + "      \"timestamp-ms\" : 24,\n"
-            + "      \"version-id\" : 2\n"
-            + "    }, {\n"
             + "      \"timestamp-ms\" : 25,\n"
             + "      \"version-id\" : 3\n"
             + "    } ]\n"
diff --git a/core/src/test/java/org/apache/iceberg/view/TestViewMetadata.java 
b/core/src/test/java/org/apache/iceberg/view/TestViewMetadata.java
index e60fe3b285..70f87e3974 100644
--- a/core/src/test/java/org/apache/iceberg/view/TestViewMetadata.java
+++ b/core/src/test/java/org/apache/iceberg/view/TestViewMetadata.java
@@ -23,7 +23,6 @@ import static 
org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 import org.apache.iceberg.MetadataUpdate;
 import org.apache.iceberg.Schema;
@@ -63,8 +62,11 @@ public class TestViewMetadata {
     ViewVersion v2 = newViewVersion(2, "select count(1) as count from t2");
     Map<Integer, ViewVersion> versionsById = ImmutableMap.of(1, v1, 2, v2, 3, 
v3);
 
-    List<ViewVersion> retainedVersions = 
ViewMetadata.Builder.expireVersions(versionsById, 2);
-    assertThat(retainedVersions).hasSameElementsAs(ImmutableList.of(v2, v3));
+    assertThat(ViewMetadata.Builder.expireVersions(versionsById, 3))
+        .containsExactlyInAnyOrder(v1, v2, v3);
+    assertThat(ViewMetadata.Builder.expireVersions(versionsById, 2))
+        .containsExactlyInAnyOrder(v2, v3);
+    assertThat(ViewMetadata.Builder.expireVersions(versionsById, 
1)).containsExactly(v3);
   }
 
   @Test
@@ -73,26 +75,38 @@ public class TestViewMetadata {
     ViewVersion v2 = newViewVersion(2, "select count(1) as count from t2");
     ViewVersion v3 = newViewVersion(3, "select count from t1");
 
-    Set<Integer> versionsById = ImmutableSet.of(2, 3);
-
-    List<ViewHistoryEntry> history =
-        ImmutableList.of(
-            ImmutableViewHistoryEntry.builder()
-                .versionId(v1.versionId())
-                .timestampMillis(v1.timestampMillis())
-                .build(),
-            ImmutableViewHistoryEntry.builder()
-                .versionId(v2.versionId())
-                .timestampMillis(v2.timestampMillis())
-                .build(),
-            ImmutableViewHistoryEntry.builder()
-                .versionId(v3.versionId())
-                .timestampMillis(v3.timestampMillis())
-                .build());
-
-    List<ViewHistoryEntry> retainedHistory =
-        ViewMetadata.Builder.updateHistory(history, versionsById);
-    assertThat(retainedHistory).hasSameElementsAs(history.subList(1, 3));
+    ViewHistoryEntry one =
+        ImmutableViewHistoryEntry.builder()
+            .versionId(v1.versionId())
+            .timestampMillis(v1.timestampMillis())
+            .build();
+    ViewHistoryEntry two =
+        ImmutableViewHistoryEntry.builder()
+            .versionId(v2.versionId())
+            .timestampMillis(v2.timestampMillis())
+            .build();
+    ViewHistoryEntry three =
+        ImmutableViewHistoryEntry.builder()
+            .versionId(v3.versionId())
+            .timestampMillis(v3.timestampMillis())
+            .build();
+
+    assertThat(
+            ViewMetadata.Builder.updateHistory(
+                ImmutableList.of(one, two, three), ImmutableSet.of(1, 2, 3)))
+        .containsExactly(one, two, three);
+
+    // one was an invalid entry in the history, so all previous elements are 
removed
+    assertThat(
+            ViewMetadata.Builder.updateHistory(
+                ImmutableList.of(three, two, one, two, three), 
ImmutableSet.of(2, 3)))
+        .containsExactly(two, three);
+
+    // two was an invalid entry in the history, so all previous elements are 
removed
+    assertThat(
+            ViewMetadata.Builder.updateHistory(
+                ImmutableList.of(one, two, three, one, three), 
ImmutableSet.of(1, 3)))
+        .containsExactly(three, one, three);
   }
 
   @Test
@@ -233,7 +247,7 @@ public class TestViewMetadata {
   }
 
   @Test
-  public void viewHistoryNormalization() {
+  public void viewVersionHistoryNormalization() {
     Map<String, String> properties = 
ImmutableMap.of(ViewProperties.VERSION_HISTORY_SIZE, "2");
     ViewVersion viewVersionOne = newViewVersion(1, "select * from ns.tbl");
     ViewVersion viewVersionTwo = newViewVersion(2, "select count(*) from 
ns.tbl");
@@ -252,12 +266,12 @@ public class TestViewMetadata {
 
     // the first build will not expire versions that were added in the builder
     assertThat(originalViewMetadata.versions()).hasSize(3);
-    assertThat(originalViewMetadata.history()).hasSize(3);
+    assertThat(originalViewMetadata.history()).hasSize(1);
 
     // rebuild the metadata to expire older versions
     ViewMetadata viewMetadata = 
ViewMetadata.buildFrom(originalViewMetadata).build();
     assertThat(viewMetadata.versions()).hasSize(2);
-    assertThat(viewMetadata.history()).hasSize(2);
+    assertThat(viewMetadata.history()).hasSize(1);
 
     // make sure that metadata changes reflect the current state after the 
history was adjusted,
     // meaning that the first view version shouldn't be included
@@ -314,6 +328,74 @@ public class TestViewMetadata {
         .isEqualTo(-1);
   }
 
+  @Test
+  public void viewVersionHistoryIsCorrectlyRetained() {
+    Map<String, String> properties = 
ImmutableMap.of(ViewProperties.VERSION_HISTORY_SIZE, "2");
+    ViewVersion viewVersionOne = newViewVersion(1, "select * from ns.tbl");
+    ViewVersion viewVersionTwo = newViewVersion(2, "select count(*) from 
ns.tbl");
+    ViewVersion viewVersionThree = newViewVersion(3, "select count(*) as count 
from ns.tbl");
+
+    ViewMetadata originalViewMetadata =
+        ViewMetadata.builder()
+            .setProperties(properties)
+            .setLocation("location")
+            .addSchema(new Schema(Types.NestedField.required(1, "x", 
Types.LongType.get())))
+            .addVersion(viewVersionOne)
+            .addVersion(viewVersionTwo)
+            .addVersion(viewVersionThree)
+            .setCurrentVersionId(3)
+            .build();
+
+    assertThat(originalViewMetadata.versions())
+        .hasSize(3)
+        .containsExactlyInAnyOrder(viewVersionOne, viewVersionTwo, 
viewVersionThree);
+    assertThat(originalViewMetadata.history())
+        .hasSize(1)
+        .first()
+        .extracting(ViewHistoryEntry::versionId)
+        .isEqualTo(3);
+
+    // rebuild the metadata to expire older versions
+    ViewMetadata viewMetadata = 
ViewMetadata.buildFrom(originalViewMetadata).build();
+    assertThat(viewMetadata.versions())
+        .hasSize(2)
+        // there is no requirement about the order of versions
+        .containsExactlyInAnyOrder(viewVersionThree, viewVersionTwo);
+    assertThat(viewMetadata.history())
+        .hasSize(1)
+        .first()
+        .extracting(ViewHistoryEntry::versionId)
+        .isEqualTo(3);
+
+    ViewMetadata updated = 
ViewMetadata.buildFrom(viewMetadata).setCurrentVersionId(2).build();
+    assertThat(updated.versions())
+        .hasSize(2)
+        .containsExactlyInAnyOrder(viewVersionTwo, viewVersionThree);
+    assertThat(updated.history())
+        .hasSize(2)
+        .element(0)
+        .extracting(ViewHistoryEntry::versionId)
+        .isEqualTo(3);
+    
assertThat(updated.history()).element(1).extracting(ViewHistoryEntry::versionId).isEqualTo(2);
+
+    ViewMetadata view = 
ViewMetadata.buildFrom(updated).setCurrentVersionId(3).build();
+    assertThat(view.versions())
+        .hasSize(2)
+        .containsExactlyInAnyOrder(viewVersionTwo, viewVersionThree);
+    assertThat(view.history())
+        .hasSize(3)
+        .element(0)
+        .extracting(ViewHistoryEntry::versionId)
+        .isEqualTo(3);
+    
assertThat(view.history()).element(1).extracting(ViewHistoryEntry::versionId).isEqualTo(2);
+    
assertThat(view.history()).element(2).extracting(ViewHistoryEntry::versionId).isEqualTo(3);
+
+    // viewVersionId 1 has been removed from versions, so this should fail
+    assertThatThrownBy(() -> 
ViewMetadata.buildFrom(view).setCurrentVersionId(1).build())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Cannot set current version to unknown version: 1");
+  }
+
   @Test
   public void viewMetadataAndMetadataChanges() {
     Map<String, String> properties = ImmutableMap.of("key1", "prop1", "key2", 
"prop2");
@@ -340,7 +422,7 @@ public class TestViewMetadata {
     assertThat(viewMetadata.versions())
         .hasSize(3)
         .containsExactly(viewVersionOne, viewVersionTwo, viewVersionThree);
-    assertThat(viewMetadata.history()).hasSize(3);
+    assertThat(viewMetadata.history()).hasSize(1);
     assertThat(viewMetadata.currentVersionId()).isEqualTo(3);
     assertThat(viewMetadata.currentVersion()).isEqualTo(viewVersionThree);
     
assertThat(viewMetadata.formatVersion()).isEqualTo(ViewMetadata.DEFAULT_VIEW_FORMAT_VERSION);
diff --git 
a/core/src/test/java/org/apache/iceberg/view/TestViewMetadataParser.java 
b/core/src/test/java/org/apache/iceberg/view/TestViewMetadataParser.java
index d237236372..7784fdc4ed 100644
--- a/core/src/test/java/org/apache/iceberg/view/TestViewMetadataParser.java
+++ b/core/src/test/java/org/apache/iceberg/view/TestViewMetadataParser.java
@@ -101,16 +101,20 @@ public class TestViewMetadataParser {
 
     String json = 
readViewMetadataInputFile("org/apache/iceberg/view/ValidViewMetadata.json");
     ViewMetadata expectedViewMetadata =
-        ViewMetadata.builder()
-            .assignUUID("fa6506c3-7681-40c8-86dc-e36561f83385")
-            .addSchema(TEST_SCHEMA)
-            .addVersion(version1)
-            .addVersion(version2)
-            .setLocation("s3://bucket/test/location")
-            .setProperties(
-                ImmutableMap.of("some-key", "some-value", 
ViewProperties.COMMENT, "some-comment"))
+        ViewMetadata.buildFrom(
+                ViewMetadata.builder()
+                    .assignUUID("fa6506c3-7681-40c8-86dc-e36561f83385")
+                    .addSchema(TEST_SCHEMA)
+                    .addVersion(version1)
+                    .addVersion(version2)
+                    .setLocation("s3://bucket/test/location")
+                    .setProperties(
+                        ImmutableMap.of(
+                            "some-key", "some-value", ViewProperties.COMMENT, 
"some-comment"))
+                    .setCurrentVersionId(1)
+                    .upgradeFormatVersion(1)
+                    .build())
             .setCurrentVersionId(2)
-            .upgradeFormatVersion(1)
             .build();
 
     ViewMetadata actual = ViewMetadataParser.fromJson(json);
@@ -213,17 +217,23 @@ public class TestViewMetadataParser {
     String metadataLocation = 
"s3://bucket/test/location/metadata/v1.metadata.json";
     ViewMetadata expectedViewMetadata =
         ViewMetadata.buildFrom(
-                ViewMetadata.builder()
-                    .assignUUID("fa6506c3-7681-40c8-86dc-e36561f83385")
-                    .addSchema(TEST_SCHEMA)
-                    .addVersion(version1)
-                    .addVersion(version2)
-                    .setLocation("s3://bucket/test/location")
-                    .setProperties(
-                        ImmutableMap.of(
-                            "some-key", "some-value", ViewProperties.COMMENT, 
"some-comment"))
+                ViewMetadata.buildFrom(
+                        ViewMetadata.builder()
+                            .assignUUID("fa6506c3-7681-40c8-86dc-e36561f83385")
+                            .addSchema(TEST_SCHEMA)
+                            .addVersion(version1)
+                            .addVersion(version2)
+                            .setLocation("s3://bucket/test/location")
+                            .setProperties(
+                                ImmutableMap.of(
+                                    "some-key",
+                                    "some-value",
+                                    ViewProperties.COMMENT,
+                                    "some-comment"))
+                            .setCurrentVersionId(1)
+                            .upgradeFormatVersion(1)
+                            .build())
                     .setCurrentVersionId(2)
-                    .upgradeFormatVersion(1)
                     .build())
             .setMetadataLocation(metadataLocation)
             .build();

Reply via email to