Fix concurrent table commits failing with a fatal 400 instead of a retryable 409 - #5138
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes Polaris’ Iceberg REST commit handling so concurrency-triggered RetryableValidationException is surfaced as a retryable conflict (HTTP 409 via CommitFailedException) rather than a fatal validation error (HTTP 400 via ValidationException), aligning behavior with Iceberg’s catalog handler semantics.
Changes:
- Catch
RetryableValidationExceptionwhile applyingMetadataUpdates and rethrow asCommitFailedExceptionin the single-table commit path (CatalogHandlerUtils.commit). - Add equivalent handling in the multi-table
commitTransactionpath (IcebergCatalogHandler.commitTransaction). - Add targeted tests asserting retryable validation failures do not leak as
ValidationException.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/CatalogHandlerUtils.java | Wraps RetryableValidationException from update application to avoid mapping to HTTP 400. |
| runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java | Adds RetryableValidationException handling in commitTransaction update application loop. |
| runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractLocalIcebergCatalogTest.java | Adds unit-style test ensuring CatalogHandlerUtils.commit surfaces retryable validation as CommitFailedException. |
| runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/CommitTransactionTest.java | Adds REST-level test ensuring commitTransaction surfaces retryable validation as CommitFailedException. |
| throw new ValidationFailureException( | ||
| new CommitFailedException( | ||
| e, "Validation failed, please retry: %s", e.getMessage())); |
There was a problem hiding this comment.
I added a comment above but this suggestion is wrong, Server-side retry won’t help because the stale sequence number is in the request payload itself; the client must refresh metadata and retry. This matches upstream Iceberg CatalogHandlers.commit()
| } catch (RetryableValidationException e) { | ||
| // Surface as a retryable 409, matching CatalogHandlerUtils.commit. | ||
| throw new CommitFailedException( | ||
| e, "Validation failed, please retry: %s", e.getMessage()); | ||
| } |
83772db to
790ff4a
Compare
flyrain
left a comment
There was a problem hiding this comment.
LGTM overall. A minor comment on the changelog.
|
Thanks, @ayushtkn! I'll merge this tomorrow if there are no further comments. |
Polaris'
CatalogHandlerUtilsreplicates Iceberg'sCatalogHandlers(as of 1.8.0) and never picked up theRetryableValidationExceptionhandlingCatalogHandlers.commit()added later. Under concurrency (e.g. writes to different branches of a table, or staged/WAP snapshots),TableMetadata.Builder.addSnapshotthrowsRetryableValidationException— aValidationExceptionsubclass — which Polaris leaks as a fatal HTTP400instead of the retryable409the Iceberg client would retry, permanently failing a commit that should succeed on rebase. This mirrorsCatalogHandlers.commit(), wrapping the update-apply step to rethrow aCommitFailedException, in both the single-table commit and multi-table commitTransaction pathsChecklist
CHANGELOG.md(if needed)site/content/in-dev/unreleased(if needed)