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

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


The following commit(s) were added to refs/heads/main by this push:
     new e9267b67e Respond with 409 in case of concurrent Namespace update 
failures instead of 500 (#1989)
e9267b67e is described below

commit e9267b67e71a5ef492649f7615e55f9ab59bc7dd
Author: fabio-rizzo-01 <fabio.rizzocas...@jpmorgan.com>
AuthorDate: Thu Jul 24 20:13:21 2025 +0100

    Respond with 409 in case of concurrent Namespace update failures instead of 
500 (#1989)
---
 .../core/exceptions/CommitConflictException.java   | 38 ++++++++++++++++++++++
 .../service/catalog/iceberg/IcebergCatalog.java    |  5 +--
 .../service/exception/PolarisExceptionMapper.java  | 31 ++++++++----------
 .../service/exception/ExceptionMapperTest.java     | 10 ++++++
 4 files changed, 65 insertions(+), 19 deletions(-)

diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/exceptions/CommitConflictException.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/exceptions/CommitConflictException.java
new file mode 100644
index 000000000..75a61204e
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/exceptions/CommitConflictException.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.polaris.core.exceptions;
+
+import com.google.errorprone.annotations.FormatMethod;
+
+public class CommitConflictException extends PolarisException {
+  @FormatMethod
+  public CommitConflictException(String message, Object... args) {
+    super(String.format(message, args));
+  }
+
+  @FormatMethod
+  public CommitConflictException(Throwable cause, String message, Object... 
args) {
+    super(String.format(message, args), cause);
+  }
+
+  public CommitConflictException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java
 
b/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java
index e2f4d96bd..7300d21c3 100644
--- 
a/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java
+++ 
b/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java
@@ -104,6 +104,7 @@ import org.apache.polaris.core.entity.PolarisEntitySubType;
 import org.apache.polaris.core.entity.PolarisEntityType;
 import org.apache.polaris.core.entity.PolarisTaskConstants;
 import org.apache.polaris.core.entity.table.IcebergTableLikeEntity;
+import org.apache.polaris.core.exceptions.CommitConflictException;
 import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
 import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper;
 import org.apache.polaris.core.persistence.ResolvedPolarisEntity;
@@ -715,7 +716,7 @@ public class IcebergCatalog extends BaseMetastoreViewCatalog
             .map(PolarisEntity::new)
             .orElse(null);
     if (returnedEntity == null) {
-      throw new RuntimeException("Concurrent modification of namespace: " + 
namespace);
+      throw new CommitConflictException("Concurrent modification of namespace: 
%s", namespace);
     }
     return true;
   }
@@ -747,7 +748,7 @@ public class IcebergCatalog extends BaseMetastoreViewCatalog
             .map(PolarisEntity::new)
             .orElse(null);
     if (returnedEntity == null) {
-      throw new RuntimeException("Concurrent modification of namespace: " + 
namespace);
+      throw new CommitConflictException("Concurrent modification of namespace: 
%s", namespace);
     }
     return true;
   }
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
 
b/service/common/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
index dfbd4f6c5..8b68d3890 100644
--- 
a/service/common/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
+++ 
b/service/common/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
@@ -24,6 +24,7 @@ import jakarta.ws.rs.ext.ExceptionMapper;
 import jakarta.ws.rs.ext.Provider;
 import org.apache.iceberg.rest.responses.ErrorResponse;
 import org.apache.polaris.core.exceptions.AlreadyExistsException;
+import org.apache.polaris.core.exceptions.CommitConflictException;
 import org.apache.polaris.core.exceptions.PolarisException;
 import org.apache.polaris.core.persistence.PolicyMappingAlreadyExistsException;
 import org.apache.polaris.core.policy.exceptions.NoSuchPolicyException;
@@ -45,23 +46,19 @@ public class PolarisExceptionMapper implements 
ExceptionMapper<PolarisException>
   private static final Logger LOGGER = 
LoggerFactory.getLogger(PolarisExceptionMapper.class);
 
   private Response.Status getStatus(PolarisException exception) {
-    if (exception instanceof AlreadyExistsException) {
-      return Response.Status.CONFLICT;
-    } else if (exception instanceof InvalidPolicyException) {
-      return Response.Status.BAD_REQUEST;
-    } else if (exception instanceof PolicyAttachException) {
-      return Response.Status.BAD_REQUEST;
-    } else if (exception instanceof NoSuchPolicyException) {
-      return Response.Status.NOT_FOUND;
-    } else if (exception instanceof PolicyVersionMismatchException) {
-      return Response.Status.CONFLICT;
-    } else if (exception instanceof PolicyMappingAlreadyExistsException) {
-      return Response.Status.CONFLICT;
-    } else if (exception instanceof PolicyInUseException) {
-      return Response.Status.BAD_REQUEST;
-    } else {
-      return Response.Status.INTERNAL_SERVER_ERROR;
-    }
+    return switch (exception) {
+      case AlreadyExistsException alreadyExistsException -> 
Response.Status.CONFLICT;
+      case CommitConflictException commitConflictException -> 
Response.Status.CONFLICT;
+      case InvalidPolicyException invalidPolicyException -> 
Response.Status.BAD_REQUEST;
+      case PolicyAttachException policyAttachException -> 
Response.Status.BAD_REQUEST;
+      case NoSuchPolicyException noSuchPolicyException -> 
Response.Status.NOT_FOUND;
+      case PolicyVersionMismatchException policyVersionMismatchException ->
+          Response.Status.CONFLICT;
+      case PolicyMappingAlreadyExistsException 
policyMappingAlreadyExistsException ->
+          Response.Status.CONFLICT;
+      case PolicyInUseException policyInUseException -> 
Response.Status.BAD_REQUEST;
+      default -> Response.Status.INTERNAL_SERVER_ERROR;
+    };
   }
 
   @Override
diff --git 
a/service/common/src/test/java/org/apache/polaris/service/exception/ExceptionMapperTest.java
 
b/service/common/src/test/java/org/apache/polaris/service/exception/ExceptionMapperTest.java
index bc5f84c1d..6dc5f77d9 100644
--- 
a/service/common/src/test/java/org/apache/polaris/service/exception/ExceptionMapperTest.java
+++ 
b/service/common/src/test/java/org/apache/polaris/service/exception/ExceptionMapperTest.java
@@ -25,11 +25,14 @@ import ch.qos.logback.classic.spi.IThrowableProxy;
 import ch.qos.logback.core.read.ListAppender;
 import com.fasterxml.jackson.core.JsonLocation;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.ext.ExceptionMapper;
 import java.util.Optional;
 import java.util.stream.Stream;
 import org.apache.polaris.core.exceptions.AlreadyExistsException;
+import org.apache.polaris.core.exceptions.CommitConflictException;
 import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -74,6 +77,13 @@ public class ExceptionMapperTest {
         .isTrue();
   }
 
+  @Test
+  public void testNamespaceException() {
+    PolarisExceptionMapper mapper = new PolarisExceptionMapper();
+    Response response = mapper.toResponse(new CommitConflictException("test"));
+    Assertions.assertThat(response.getStatus()).isEqualTo(409);
+  }
+
   static Stream<Arguments> testFullExceptionIsLogged() {
     // ConstraintViolationException isn't included because it doesn't 
propagate any info to its
     // inherited Exception

Reply via email to