aiskakov28 opened a new pull request, #18760: URL: https://github.com/apache/iceberg/pull/18760
When several clients create the same namespace at once, `JdbcCatalog.createNamespace` can let more than one of them pass its `namespaceExists` check. The first properties insert wins, and the others fail on the primary key of `iceberg_namespace_properties`. That `SQLException` comes out as `UncheckedSQLException`, which REST servers return as a 500, so the client can't tell that its create was simply rejected because the namespace already exists. This catches the failed insert and checks again whether the namespace exists. If it does, `createNamespace` throws `AlreadyExistsException`, the same as when the first check finds it, and the REST layer returns 409. If the recheck succeeds and the namespace still doesn't exist, the original exception is rethrown, so other insert failures are reported as before. It doesn't depend on database-specific error codes. iceberg-go fixed the same race in its SQL catalog the same way (apache/iceberg-go#1635). The new test, `testCreateNamespaceLosingConcurrentCreate`, is deterministic: it creates the namespace, then uses a spy so that the second create's first `namespaceExists` check returns `false`, as it would for a create that ran its check just before the winner's insert. Without the change it fails with `UncheckedSQLException: Failed to execute: INSERT INTO iceberg_namespace_properties ...`; with it, the create throws `AlreadyExistsException` and the winner's properties are unchanged. Closes #18759. **Testing** - `./gradlew :iceberg-core:test --tests org.apache.iceberg.jdbc.TestJdbcCatalog` on JDK 21: 157 tests, 0 failures, 12 skipped. The new test fails without the change. - `./gradlew :iceberg-core:spotlessCheck` passes. - End to end, with `apache/iceberg-rest-fixture` images built from this commit with and without the change (`./gradlew :iceberg-open-api:shadowJar` and the fixture Dockerfile): 6 clients creating the same namespace, 20 rounds, 4 runs each. Without the change, every round had one 200 and 18–57 of the 120 creates per run got a 500. With it, every round had one 200 and every other create got a 409, with no 500s. Found by @aiskakov28 and @aldusialn. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
