Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Do not report publication success before the REST state is reconciled

Returning normally from these catches tells IcebergCommitCallback that the external catalog serves the new head. It then runs deleteApplicableMetadataFiles and expireManifestList immediately (callback lines 1266–1274; the no-base path also calls expireAllBefore). For example, with Iceberg snapshot retention min=max=2, REST snapshot 2 still advertises snapshots 1 and 2. If publication of snapshot 3 is rejected here, cleanup can delete snapshot 1's manifest list even though the REST metadata never removed that snapshot, breaking catalog time-travel reads. On a final/idle commit there is also no guaranteed next attempt: Flink clears the successful committable and the callback retains no durable retry obligation. Preserve failed/unknown publication state so cleanup cannot remove externally referenced files, and either reconcile/retry before success or retain a durable retry task with a verified retention boundary. Add a rejected-publication test that asserts every file referenced by the unchanged REST metadata remains readable.

// 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,
Expand Down
Loading