This is an automated email from the ASF dual-hosted git repository.
nastra 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 9f04b05090 Nessie: Check namespace existence at the ref HEAD in
listNamespaces (#18035)
9f04b05090 is described below
commit 9f04b05090ade75d50eef5f244914181393adbb7
Author: Eunbin Son <[email protected]>
AuthorDate: Fri Nov 6 00:29:49 2026 +0900
Nessie: Check namespace existence at the ref HEAD in listNamespaces (#18035)
listNamespaces(Namespace) mixed two commit points inside one method. The
parent namespace existence check called
api.getContent().reference(getReference()),
which pins the request to the hash the client cached when it first resolved
the reference. The child listing right below it used
withReference(api.getEntries()),
which sends only the branch name for a mutable reference so the server
answers
from the HEAD of the ref. A client that had not seen a newer commit
therefore
got NoSuchNamespaceException for a namespace another client had just
created,
even though the child listing would have found it.
Use the withReference(api.getContent()) idiom for the existence check so
both
halves of the method read the same commit. loadNamespaceMetadata() in the
same
class already reads namespace content that way.
The three remaining api.getContent().reference(getReference()) calls in this
class (createNamespace, dropNamespace, updateProperties) are left alone.
They
are pre-checks that run right before a commit against that same hash, so
pinning the hash there is the intended behavior.
Generated-by: Claude Code
* Nessie: Reword test comment and use containsExactly in TestMultipleClients
Review follow-up for listNestedNamespacesFromAnotherClient. The first
listNamespaces() call does not pin the other client to a hash; it only
forces the client to resolve its reference before the commits below so
the hash it caches is older than those commits. Say that instead of
"pins", and use containsExactly since the listing has a single entry.
Generated-by: Claude Code
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../org/apache/iceberg/nessie/NessieIcebergClient.java | 6 +-----
.../org/apache/iceberg/nessie/TestMultipleClients.java | 14 ++++++++++++++
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git
a/nessie/src/main/java/org/apache/iceberg/nessie/NessieIcebergClient.java
b/nessie/src/main/java/org/apache/iceberg/nessie/NessieIcebergClient.java
index a59db82daa..9616855a71 100644
--- a/nessie/src/main/java/org/apache/iceberg/nessie/NessieIcebergClient.java
+++ b/nessie/src/main/java/org/apache/iceberg/nessie/NessieIcebergClient.java
@@ -272,11 +272,7 @@ public class NessieIcebergClient implements AutoCloseable {
org.projectnessie.model.Namespace root =
org.projectnessie.model.Namespace.of(namespace.levels());
Content existing =
- api.getContent()
- .reference(getReference())
- .key(root.toContentKey())
- .get()
- .get(root.toContentKey());
+
withReference(api.getContent()).key(root.toContentKey()).get().get(root.toContentKey());
if (existing == null) {
throw new NoSuchNamespaceException("Namespace does not exist: %s",
namespace);
}
diff --git
a/nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java
b/nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java
index 883dc9d2f0..43dd9fc0ae 100644
--- a/nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java
+++ b/nessie/src/test/java/org/apache/iceberg/nessie/TestMultipleClients.java
@@ -111,6 +111,20 @@ public class TestMultipleClients extends BaseTestIceberg {
"Cannot list child namespaces from 'db1': ref '%s' is no longer
valid", branch);
}
+ @Test
+ void listNestedNamespacesFromAnotherClient() {
+ // make the other client resolve its reference now, so the hash it caches
is older than the
+ // commits below
+ assertThat(anotherCatalog.listNamespaces()).isEmpty();
+
+ catalog.createNamespace(Namespace.of("db1"), Collections.emptyMap());
+ catalog.createNamespace(Namespace.of("db1", "sub1"),
Collections.emptyMap());
+
+ // the parent check must read the HEAD of the ref, not the hash the other
client cached
+ assertThat(anotherCatalog.listNamespaces(Namespace.of("db1")))
+ .containsExactly(Namespace.of("db1", "sub1"));
+ }
+
@Test
public void testLoadNamespaceMetadata() throws NessieConflictException,
NessieNotFoundException {
assertThatThrownBy(() ->
catalog.loadNamespaceMetadata(Namespace.of("namespace1")))