|
18 | 18 | */ |
19 | 19 | package org.apache.iceberg; |
20 | 20 |
|
| 21 | +import static org.apache.iceberg.TestBase.FILE_A; |
| 22 | +import static org.apache.iceberg.TestBase.SCHEMA; |
| 23 | +import static org.apache.iceberg.TestBase.SPEC; |
21 | 24 | import static org.assertj.core.api.Assertions.assertThat; |
22 | 25 |
|
| 26 | +import java.io.File; |
| 27 | +import java.nio.file.Paths; |
23 | 28 | import org.junit.jupiter.api.Test; |
| 29 | +import org.junit.jupiter.api.io.TempDir; |
24 | 30 |
|
25 | 31 | public class TestSnapshotProducer { |
26 | 32 |
|
@@ -74,4 +80,67 @@ private void assertManifestWriterCount( |
74 | 80 | int writerCount = SnapshotProducer.manifestWriterCount(workerPoolSize, fileCount); |
75 | 81 | assertThat(writerCount).as(errMsg).isEqualTo(expectedManifestWriterCount); |
76 | 82 | } |
| 83 | + |
| 84 | + @Test |
| 85 | + public void manifestNotCleanedUpWhenSnapshotNotLoadableAfterCommit(@TempDir File tableDir) { |
| 86 | + // Uses a custom TableOps that returns stale metadata (without the new snapshot) on the |
| 87 | + // first refresh() after commit, simulating eventual consistency. Verifies that commit succeeds |
| 88 | + // and that the committed data is visible once the table is refreshed again |
| 89 | + String tableName = "stale-table-on-first-refresh"; |
| 90 | + TestTables.TestTableOperations ops = opsWithStaleRefreshAfterCommit(tableName, tableDir); |
| 91 | + TestTables.TestTable tableWithStaleRefresh = |
| 92 | + TestTables.create(tableDir, tableName, SCHEMA, SPEC, SortOrder.unsorted(), 2, ops); |
| 93 | + |
| 94 | + // the first refresh() after the commit will return stale metadata (without this snapshot), so |
| 95 | + // SnapshotProducer will skip cleanup to avoid accidentally deleting files that are part of the |
| 96 | + // committed snapshot but commit still succeeds |
| 97 | + tableWithStaleRefresh.newAppend().appendFile(FILE_A).commit(); |
| 98 | + |
| 99 | + // Refresh again to get the real metadata; the snapshot must be visible now |
| 100 | + tableWithStaleRefresh.ops().refresh(); |
| 101 | + Snapshot snapshot = tableWithStaleRefresh.currentSnapshot(); |
| 102 | + assertThat(snapshot) |
| 103 | + .as("Committed snapshot must be visible after refresh (eventual consistency resolved)") |
| 104 | + .isNotNull(); |
| 105 | + |
| 106 | + File metadata = Paths.get(tableDir.getPath(), "metadata").toFile(); |
| 107 | + assertThat(snapshot.allManifests(tableWithStaleRefresh.io())) |
| 108 | + .isNotEmpty() |
| 109 | + .allSatisfy( |
| 110 | + manifest -> assertThat(metadata.listFiles()).contains(new File(manifest.path()))); |
| 111 | + } |
| 112 | + |
| 113 | + /** |
| 114 | + * Creates a TableOperations that returns stale metadata (without the newly committed snapshot) on |
| 115 | + * the first refresh() after a commit. This simulates eventual consistency where the committed |
| 116 | + * snapshot is not yet visible. Used to verify that when the snapshot cannot be loaded after |
| 117 | + * commit, cleanup is skipped to avoid accidentally deleting files that are part of the committed |
| 118 | + * snapshot. |
| 119 | + */ |
| 120 | + private static TestTables.TestTableOperations opsWithStaleRefreshAfterCommit( |
| 121 | + String name, File location) { |
| 122 | + return new TestTables.TestTableOperations(name, location) { |
| 123 | + private TableMetadata metadataToReturnOnNextRefresh; |
| 124 | + |
| 125 | + @Override |
| 126 | + public void commit(TableMetadata base, TableMetadata updatedMetadata) { |
| 127 | + super.commit(base, updatedMetadata); |
| 128 | + if (base != null) { |
| 129 | + // return stale metadata on the first refresh() call |
| 130 | + this.metadataToReturnOnNextRefresh = base; |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + @Override |
| 135 | + public TableMetadata refresh() { |
| 136 | + if (metadataToReturnOnNextRefresh != null) { |
| 137 | + this.current = metadataToReturnOnNextRefresh; |
| 138 | + this.metadataToReturnOnNextRefresh = null; |
| 139 | + return current; |
| 140 | + } |
| 141 | + |
| 142 | + return super.refresh(); |
| 143 | + } |
| 144 | + }; |
| 145 | + } |
77 | 146 | } |
0 commit comments