Skip to content

Commit 2be228d

Browse files
authored
Bump Nessie to 0.8.2 + related changes (#2588)
* Bump Nessie to 0.8.2 + replace Gradle plugin with new JUnit extension More changes in this PR in following commits. Replace Gradle plugin with new JUnit extension. See [Add JAX-RS tests and add JUnit/Jupyter extension](projectnessie/nessie#1566) * Changes required by Nessie-API changes Apply changes to Iceberg required by API changes in Nessie: * [Re-introduce wrapper classes for query params of CommitLog/Entries](projectnessie/nessie#1595) * [Server-side commit range filtering](projectnessie/nessie#1596) * [Add hashOnRef query param to support time travel on a named ref](projectnessie/nessie#1589) * [Only accept NamedRefs in REST API](projectnessie/nessie#1583) * Bugfix: must send the Contents.id of the existing table Nessie's `Contents.id` is a random ID generated when the `Contents.Key` is first used (think: CREATE TABLE) and must not be changed. This change addresses a bug in the Iceberg-Nesie code that caused a new id for every change. * Throw `CommitStateUnknownException` for `renameTable` as well Follow-up of #2515 * Fix race-condition & save one roundtrip to Nessie during "commit" When commiting a change, the Nessie-API now returns the hash of the commit for the change. This returned hash should then be used as the "expected hash" for the next commit. The previous approach was to commit the change to Nessie and then do another request to retrieve the new hash of HEAD. This old approach is prone to a race condition, namely when another commit happens after "this" commit but before retrieving the "new HEAD", so "this" instance would wrongly ignore the other commit's changes during conflict checks. See [Let VersionStore.create()+commit() return the current hash](projectnessie/nessie#1089)
1 parent 3f158ff commit 2be228d

File tree

11 files changed

+95
-60
lines changed

11 files changed

+95
-60
lines changed

build.gradle

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ buildscript {
3535

3636
plugins {
3737
id 'nebula.dependency-recommender' version '9.0.2'
38-
id 'org.projectnessie' version '0.5.1'
3938
}
4039

4140
try {
@@ -127,6 +126,7 @@ subprojects {
127126
compile 'com.github.stephenc.findbugs:findbugs-annotations'
128127

129128
testCompile 'org.junit.vintage:junit-vintage-engine'
129+
testCompile 'org.junit.jupiter:junit-jupiter-engine'
130130
testCompile 'org.junit.jupiter:junit-jupiter'
131131
testCompile 'org.slf4j:slf4j-simple'
132132
testCompile 'org.mockito:mockito-core'
@@ -1266,19 +1266,18 @@ project(':iceberg-pig') {
12661266
}
12671267

12681268
project(':iceberg-nessie') {
1269-
apply plugin: 'org.projectnessie'
1269+
test {
1270+
useJUnitPlatform()
1271+
}
12701272

12711273
dependencies {
12721274
compile project(':iceberg-core')
12731275
compile project(path: ':iceberg-bundled-guava', configuration: 'shadow')
12741276
compile "org.projectnessie:nessie-client"
12751277

1276-
// dependency version "recommendation" via nebula.dependency-recommender don't work here "immediately",
1277-
// so pull the Quarkus + Nessie versions from the root-project
1278-
def quarkusVersion = rootProject.dependencyRecommendations.getRecommendedVersion("io.quarkus", "quarkus-bom")
1279-
def nessieVersion = rootProject.dependencyRecommendations.getRecommendedVersion("org.projectnessie", "nessie-quarkus")
1280-
nessieQuarkusRuntime(enforcedPlatform("io.quarkus:quarkus-bom:${quarkusVersion}"))
1281-
nessieQuarkusServer "org.projectnessie:nessie-quarkus:${nessieVersion}"
1278+
testImplementation "org.projectnessie:nessie-jaxrs-testextension"
1279+
// Need to "pull in" el-api explicitly :(
1280+
testImplementation "jakarta.el:jakarta.el-api"
12821281

12831282
compileOnly "org.apache.hadoop:hadoop-common"
12841283

nessie/src/main/java/org/apache/iceberg/nessie/NessieCatalog.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
package org.apache.iceberg.nessie;
2121

22-
import java.util.Collections;
2322
import java.util.List;
2423
import java.util.Map;
2524
import java.util.Set;
@@ -37,6 +36,7 @@
3736
import org.apache.iceberg.catalog.TableIdentifier;
3837
import org.apache.iceberg.exceptions.AlreadyExistsException;
3938
import org.apache.iceberg.exceptions.CommitFailedException;
39+
import org.apache.iceberg.exceptions.CommitStateUnknownException;
4040
import org.apache.iceberg.exceptions.NamespaceNotEmptyException;
4141
import org.apache.iceberg.exceptions.NoSuchNamespaceException;
4242
import org.apache.iceberg.exceptions.NoSuchTableException;
@@ -46,11 +46,14 @@
4646
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
4747
import org.apache.iceberg.util.Tasks;
4848
import org.projectnessie.api.TreeApi;
49+
import org.projectnessie.api.params.EntriesParams;
4950
import org.projectnessie.client.NessieClient;
5051
import org.projectnessie.client.NessieConfigConstants;
52+
import org.projectnessie.client.http.HttpClientException;
5153
import org.projectnessie.error.BaseNessieClientServerException;
5254
import org.projectnessie.error.NessieConflictException;
5355
import org.projectnessie.error.NessieNotFoundException;
56+
import org.projectnessie.model.Branch;
5457
import org.projectnessie.model.Contents;
5558
import org.projectnessie.model.IcebergTable;
5659
import org.projectnessie.model.ImmutableDelete;
@@ -182,8 +185,9 @@ public boolean dropTable(TableIdentifier identifier, boolean purge) {
182185
.throwFailureWhenFinished()
183186
.onFailure((c, exception) -> refresh())
184187
.run(c -> {
185-
client.getTreeApi().commitMultipleOperations(reference.getAsBranch().getName(), reference.getHash(), c);
186-
refresh(); // note: updated to reference.updateReference() with Nessie 0.6
188+
Branch branch = client.getTreeApi().commitMultipleOperations(reference.getAsBranch().getName(),
189+
reference.getHash(), c);
190+
reference.updateReference(branch);
187191
}, BaseNessieClientServerException.class);
188192
threw = false;
189193
} catch (NessieConflictException e) {
@@ -225,8 +229,9 @@ public void renameTable(TableIdentifier from, TableIdentifier toOriginal) {
225229
.throwFailureWhenFinished()
226230
.onFailure((c, exception) -> refresh())
227231
.run(c -> {
228-
client.getTreeApi().commitMultipleOperations(reference.getAsBranch().getName(), reference.getHash(), c);
229-
refresh();
232+
Branch branch = client.getTreeApi().commitMultipleOperations(reference.getAsBranch().getName(),
233+
reference.getHash(), c);
234+
reference.updateReference(branch);
230235
}, BaseNessieClientServerException.class);
231236
} catch (NessieNotFoundException e) {
232237
// important note: the NotFoundException refers to the ref only. If a table was not found it would imply that the
@@ -236,6 +241,12 @@ public void renameTable(TableIdentifier from, TableIdentifier toOriginal) {
236241
throw new RuntimeException("Failed to drop table as ref is no longer valid.", e);
237242
} catch (BaseNessieClientServerException e) {
238243
throw new CommitFailedException(e, "Failed to rename table: the current reference is not up to date.");
244+
} catch (HttpClientException ex) {
245+
// Intentionally catch all nessie-client-exceptions here and not just the "timeout" variant
246+
// to catch all kinds of network errors (e.g. connection reset). Network code implementation
247+
// details and all kinds of network devices can induce unexpected behavior. So better be
248+
// safe than sorry.
249+
throw new CommitStateUnknownException(ex);
239250
}
240251
// Intentionally just "throw through" Nessie's HttpClientException here and do not "special case"
241252
// just the "timeout" variant to propagate all kinds of network errors (e.g. connection reset).
@@ -324,7 +335,8 @@ String currentRefName() {
324335

325336
private IcebergTable table(TableIdentifier tableIdentifier) {
326337
try {
327-
Contents table = client.getContentsApi().getContents(NessieUtil.toKey(tableIdentifier), reference.getHash());
338+
Contents table = client.getContentsApi()
339+
.getContents(NessieUtil.toKey(tableIdentifier), reference.getName(), reference.getHash());
328340
return table.unwrap(IcebergTable.class).orElse(null);
329341
} catch (NessieNotFoundException e) {
330342
return null;
@@ -353,7 +365,7 @@ private UpdateableReference loadReference(String requestedRef) {
353365
private Stream<TableIdentifier> tableStream(Namespace namespace) {
354366
try {
355367
return client.getTreeApi()
356-
.getEntries(reference.getHash(), null, null, Collections.emptyList())
368+
.getEntries(reference.getName(), EntriesParams.builder().hashOnRef(reference.getHash()).build())
357369
.getEntries()
358370
.stream()
359371
.filter(NessieUtil.namespacePredicate(namespace))

nessie/src/main/java/org/apache/iceberg/nessie/NessieTableOperations.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.projectnessie.client.http.HttpClientException;
3131
import org.projectnessie.error.NessieConflictException;
3232
import org.projectnessie.error.NessieNotFoundException;
33+
import org.projectnessie.model.Branch;
3334
import org.projectnessie.model.Contents;
3435
import org.projectnessie.model.ContentsKey;
3536
import org.projectnessie.model.IcebergTable;
@@ -80,7 +81,7 @@ protected void doRefresh() {
8081
}
8182
String metadataLocation = null;
8283
try {
83-
Contents contents = client.getContentsApi().getContents(key, reference.getHash());
84+
Contents contents = client.getContentsApi().getContents(key, reference.getName(), reference.getHash());
8485
this.table = contents.unwrap(IcebergTable.class)
8586
.orElseThrow(() ->
8687
new IllegalStateException("Cannot refresh iceberg table: " +
@@ -102,10 +103,17 @@ protected void doCommit(TableMetadata base, TableMetadata metadata) {
102103

103104
boolean delete = true;
104105
try {
105-
IcebergTable newTable = ImmutableIcebergTable.builder().metadataLocation(newMetadataLocation).build();
106-
Operations op = ImmutableOperations.builder().addOperations(Operation.Put.of(key, newTable))
106+
ImmutableIcebergTable.Builder newTable = ImmutableIcebergTable.builder();
107+
if (table != null) {
108+
newTable.from(table);
109+
}
110+
newTable.metadataLocation(newMetadataLocation);
111+
112+
Operations op = ImmutableOperations.builder().addOperations(Operation.Put.of(key, newTable.build()))
107113
.commitMeta(NessieUtil.buildCommitMetadata("iceberg commit", catalogOptions)).build();
108-
client.getTreeApi().commitMultipleOperations(reference.getAsBranch().getName(), reference.getHash(), op);
114+
Branch branch = client.getTreeApi().commitMultipleOperations(reference.getAsBranch().getName(),
115+
reference.getHash(), op);
116+
reference.updateReference(branch);
109117

110118
delete = false;
111119
} catch (NessieConflictException ex) {

nessie/src/main/java/org/apache/iceberg/nessie/UpdateableReference.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.apache.iceberg.nessie;
2121

22+
import java.util.Objects;
2223
import org.projectnessie.api.TreeApi;
2324
import org.projectnessie.error.NessieNotFoundException;
2425
import org.projectnessie.model.Branch;
@@ -44,6 +45,11 @@ public boolean refresh() throws NessieNotFoundException {
4445
return !oldReference.equals(reference);
4546
}
4647

48+
public void updateReference(Reference ref) {
49+
Objects.requireNonNull(ref);
50+
this.reference = ref;
51+
}
52+
4753
public boolean isBranch() {
4854
return reference instanceof Branch;
4955
}

nessie/src/test/java/org/apache/iceberg/nessie/BaseTestIceberg.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.apache.iceberg.nessie;
2121

2222
import java.io.IOException;
23+
import java.nio.file.Path;
2324
import java.util.ArrayList;
2425
import java.util.List;
2526
import org.apache.hadoop.conf.Configuration;
@@ -33,15 +34,16 @@
3334
import org.apache.iceberg.types.Types;
3435
import org.apache.iceberg.types.Types.LongType;
3536
import org.apache.iceberg.types.Types.StructType;
36-
import org.junit.After;
37-
import org.junit.Before;
38-
import org.junit.Rule;
39-
import org.junit.rules.TemporaryFolder;
37+
import org.junit.jupiter.api.AfterEach;
38+
import org.junit.jupiter.api.BeforeEach;
39+
import org.junit.jupiter.api.extension.RegisterExtension;
40+
import org.junit.jupiter.api.io.TempDir;
4041
import org.projectnessie.api.ContentsApi;
4142
import org.projectnessie.api.TreeApi;
4243
import org.projectnessie.client.NessieClient;
4344
import org.projectnessie.error.NessieConflictException;
4445
import org.projectnessie.error.NessieNotFoundException;
46+
import org.projectnessie.jaxrs.NessieJaxRsExtension;
4547
import org.projectnessie.model.Branch;
4648
import org.projectnessie.model.Reference;
4749
import org.slf4j.Logger;
@@ -50,11 +52,13 @@
5052
import static org.apache.iceberg.types.Types.NestedField.required;
5153

5254
public abstract class BaseTestIceberg {
55+
@RegisterExtension
56+
static NessieJaxRsExtension server = new NessieJaxRsExtension();
5357

5458
private static final Logger LOGGER = LoggerFactory.getLogger(BaseTestIceberg.class);
5559

56-
@Rule
57-
public TemporaryFolder temp = new TemporaryFolder();
60+
@TempDir
61+
public Path temp;
5862

5963
protected NessieCatalog catalog;
6064
protected NessieClient client;
@@ -79,10 +83,10 @@ private void resetData() throws NessieConflictException, NessieNotFoundException
7983
tree.createReference(Branch.of("main", null));
8084
}
8185

82-
@Before
86+
@BeforeEach
8387
public void beforeEach() throws IOException {
8488
String port = System.getProperty("quarkus.http.test-port", "19120");
85-
uri = String.format("http://localhost:%s/api/v1", port);
89+
uri = server.getURI().toString();
8690
this.client = NessieClient.builder().withUri(uri).build();
8791
tree = client.getTreeApi();
8892
contents = client.getContentsApi();
@@ -105,7 +109,7 @@ NessieCatalog initCatalog(String ref) {
105109
newCatalog.initialize("nessie", ImmutableMap.of("ref", ref,
106110
CatalogProperties.URI, uri,
107111
"auth_type", "NONE",
108-
CatalogProperties.WAREHOUSE_LOCATION, temp.getRoot().toURI().toString()
112+
CatalogProperties.WAREHOUSE_LOCATION, temp.toUri().toString()
109113
));
110114
return newCatalog;
111115
}
@@ -136,7 +140,7 @@ void createBranch(String name, String hash) throws NessieNotFoundException, Ness
136140
tree.createReference(Branch.of(name, hash));
137141
}
138142

139-
@After
143+
@AfterEach
140144
public void afterEach() throws Exception {
141145
catalog.close();
142146
client.close();

nessie/src/test/java/org/apache/iceberg/nessie/NessieUtilTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import org.apache.iceberg.CatalogProperties;
2323
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
2424
import org.assertj.core.api.Assertions;
25-
import org.junit.Test;
25+
import org.junit.jupiter.api.Test;
2626
import org.projectnessie.model.CommitMeta;
2727

2828
public class NessieUtilTest {

nessie/src/test/java/org/apache/iceberg/nessie/TestBranchVisibility.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import org.apache.iceberg.catalog.TableIdentifier;
2323
import org.apache.iceberg.types.Types;
2424
import org.assertj.core.api.Assertions;
25-
import org.junit.After;
26-
import org.junit.Before;
27-
import org.junit.Test;
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
2828
import org.projectnessie.error.NessieConflictException;
2929
import org.projectnessie.error.NessieNotFoundException;
3030

@@ -39,7 +39,7 @@ public TestBranchVisibility() {
3939
super("main");
4040
}
4141

42-
@Before
42+
@BeforeEach
4343
public void before() throws NessieNotFoundException, NessieConflictException {
4444
createTable(tableIdentifier1, 1); // table 1
4545
createTable(tableIdentifier2, 1); // table 2
@@ -48,7 +48,7 @@ public void before() throws NessieNotFoundException, NessieConflictException {
4848
testCatalog = initCatalog("test");
4949
}
5050

51-
@After
51+
@AfterEach
5252
public void after() throws NessieNotFoundException, NessieConflictException {
5353
catalog.dropTable(tableIdentifier1);
5454
catalog.dropTable(tableIdentifier2);
@@ -79,31 +79,34 @@ public void testUpdateCatalogs() {
7979
}
8080

8181
@Test
82-
public void testCatalogOnReference() throws NessieNotFoundException {
82+
public void testCatalogOnReference() {
8383
updateSchema(catalog, tableIdentifier1);
8484
updateSchema(testCatalog, tableIdentifier2);
85-
String mainHash = tree.getReferenceByName("main").getHash();
8685

8786
// catalog created with ref points to same catalog as above
8887
NessieCatalog refCatalog = initCatalog("test");
8988
testCatalogEquality(refCatalog, testCatalog, true, true);
9089

9190
// catalog created with hash points to same catalog as above
92-
NessieCatalog refHashCatalog = initCatalog(mainHash);
91+
NessieCatalog refHashCatalog = initCatalog("main");
9392
testCatalogEquality(refHashCatalog, catalog, true, true);
9493
}
9594

9695
@Test
97-
public void testCatalogWithTableNames() throws NessieNotFoundException {
96+
public void testCatalogWithTableNames() {
9897
updateSchema(testCatalog, tableIdentifier2);
99-
String mainHash = tree.getReferenceByName("main").getHash();
98+
99+
String mainName = "main";
100100

101101
// asking for table@branch gives expected regardless of catalog
102102
Assertions.assertThat(metadataLocation(catalog, TableIdentifier.of("test-ns", "table1@test")))
103103
.isEqualTo(metadataLocation(testCatalog, tableIdentifier1));
104104

105-
// asking for table@branch#hash gives expected regardless of catalog
106-
Assertions.assertThat(metadataLocation(catalog, TableIdentifier.of("test-ns", "table1@" + mainHash)))
105+
// Asking for table@branch gives expected regardless of catalog.
106+
// Earlier versions used "table1@" + tree.getReferenceByName("main").getHash() before, but since
107+
// Nessie 0.8.2 the branch name became mandatory and specifying a hash within a branch is not
108+
// possible.
109+
Assertions.assertThat(metadataLocation(catalog, TableIdentifier.of("test-ns", "table1@" + mainName)))
107110
.isEqualTo(metadataLocation(testCatalog, tableIdentifier1));
108111
}
109112

nessie/src/test/java/org/apache/iceberg/nessie/TestNamespace.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.apache.iceberg.catalog.Namespace;
2424
import org.apache.iceberg.catalog.TableIdentifier;
2525
import org.assertj.core.api.Assertions;
26-
import org.junit.Test;
26+
import org.junit.jupiter.api.Test;
2727

2828
public class TestNamespace extends BaseTestIceberg {
2929
private static final String BRANCH = "test-namespace";

0 commit comments

Comments
 (0)