diff --git a/paimon-iceberg/src/main/java/org/apache/paimon/iceberg/IcebergRestMetadataCommitter.java b/paimon-iceberg/src/main/java/org/apache/paimon/iceberg/IcebergRestMetadataCommitter.java index 1115624788dd..70860ff26bef 100644 --- a/paimon-iceberg/src/main/java/org/apache/paimon/iceberg/IcebergRestMetadataCommitter.java +++ b/paimon-iceberg/src/main/java/org/apache/paimon/iceberg/IcebergRestMetadataCommitter.java @@ -46,6 +46,8 @@ import org.apache.iceberg.catalog.Namespace; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.exceptions.AlreadyExistsException; +import org.apache.iceberg.exceptions.CommitFailedException; +import org.apache.iceberg.exceptions.CommitStateUnknownException; import org.apache.iceberg.rest.Endpoint; import org.apache.iceberg.rest.RESTCatalog; import org.apache.iceberg.types.Types; @@ -254,6 +256,37 @@ private void commitMetadataImpl( ((BaseTable) icebergTable) .operations() .commit(((BaseTable) icebergTable).operations().current(), updatedForCommit); + } catch (CommitStateUnknownException e) { + // The catalog returned an ambiguous response, so we cannot tell whether this commit + // was applied server-side. Either way the next attempt reloads the table and runs + // checkBase() against that live state: if it landed, the base matches and the next + // commit proceeds normally; if it did not, checkBase() sees the drift and the table + // is rebuilt from the current file set. Failing here does not resolve the ambiguity, + // it only takes down every other table the job is syncing. + LOG.warn( + "Commit to rest catalog returned an ambiguous response for table {}, snapshot" + + " {}; not failing the commit, the next attempt will reconcile.", + icebergTableIdentifier, + updatedForCommit.currentSnapshot() == null + ? null + : updatedForCommit.currentSnapshot().snapshotId(), + e); + } catch (CommitFailedException e) { + // The catalog rejected the compare-and-swap because the table moved between our read + // of the base metadata and this commit. Unlike the ambiguous case above this one is + // unambiguous: CommitFailedException implements CleanableFailure, so nothing landed + // server-side. It reconciles through the same path on the next attempt, and Paimon's + // own commit has already durably applied the write, so only the Iceberg metadata + // lags, by one commit. + LOG.warn( + "Commit to rest catalog was rejected for table {}, snapshot {}, because the" + + " table changed concurrently; not failing the commit, the next" + + " attempt will reconcile.", + icebergTableIdentifier, + updatedForCommit.currentSnapshot() == null + ? null + : updatedForCommit.currentSnapshot().snapshotId(), + e); } catch (Exception e) { throw new RuntimeException( "Fail to commit metadata to rest catalog for table: " + icebergTableIdentifier,