dancsi opened a new issue, #4022:
URL: https://github.com/apache/iceberg-python/issues/4022

   **Apache Iceberg version:** 0.12.0 (also reproduced on `main` at ebbc0ba3e4)
   
   ### Please describe the bug 🐞
   
   After a failed attempt, `Transaction.commit_transaction` checks whether the 
attempt actually landed:
   
   ```python
   if all(
       self._table.metadata.snapshot_by_id(producer._snapshot_id) is not None
       for producer in self._snapshot_producers
   ):
   ```
   
   A transaction can contain a producer that never creates a snapshot. 
`Table.overwrite` on an empty table (or any `delete` that matches nothing, 
followed by an append) registers a delete producer, but only the append 
produces an `AddSnapshotUpdate`. The delete producer's `_snapshot_id` never 
appears in the table, so the check is always false, even when the append landed.
   
   pyiceberg then rebuilds and retries. On the rebuilt attempt, the delete 
producer's concurrency validation sees the transaction's *own* landed append as 
a concurrent conflicting write, and raises `ValidationException`. That goes to 
`_clean_all_uncommitted()`, which deletes the manifest list and manifests of 
the snapshot that landed.
   
   ### Reproduction
   
   ```python
   import tempfile
   
   import pyarrow as pa
   from pyiceberg.catalog.sql import SqlCatalog
   from pyiceberg.exceptions import CommitFailedException
   from pyiceberg.schema import Schema
   from pyiceberg.types import LongType, NestedField
   
   warehouse = tempfile.mkdtemp()
   catalog = SqlCatalog("default", uri=f"sqlite:///{warehouse}/catalog.db", 
warehouse=f"file://{warehouse}")
   catalog.create_namespace("default")
   table = catalog.create_table(
       "default.t",
       Schema(NestedField(1, "a", LongType(), required=False)),
       properties={"commit.retry.min-wait-ms": "0"},
   )
   
   # The catalog applies the first commit, but the client sees a failure.
   commit_table = catalog.commit_table
   calls = 0
   def commit_table_lose_first_response(*args):
       global calls
       calls += 1
       response = commit_table(*args)
       if calls == 1:
           raise CommitFailedException("simulated lost response")
       return response
   catalog.commit_table = commit_table_lose_first_response
   
   try:
       table.overwrite(pa.table({"a": pa.array([1, 2, 3], pa.int64())}))
   except Exception as e:
       print(f"overwrite raised: {type(e).__name__}: {e}")
   
   table = catalog.load_table("default.t")
   snapshot = table.current_snapshot()
   print("manifest list exists:", 
table.io.new_input(snapshot.manifest_list).exists())
   table.scan().to_arrow()
   ```
   
   Output:
   
   ```
   Commit failed due to a concurrent update, retrying (1/4) in 0 ms
   overwrite raised: ValidationException: Added data files were found matching 
the filter for snapshots set()!
   manifest list exists: False
   FileNotFoundError: [Errno 2] Failed to open local file 
'.../metadata/snap-...avro'
   ```
   
   The same overwrite with a response that isn't lost succeeds, and a plain 
`append` with a lost first response is correctly detected as landed.
   
   ### Suggested fix
   
   Base the check on the snapshots the attempt actually tried to add, i.e. the 
`AddSnapshotUpdate`s in `self._updates`, rather than on every producer. 
Equivalently, skip producers that didn't produce a snapshot. Ideally, a 
`ValidationException` raised during a rebuild should also never clean up a 
snapshot that is present in the refreshed metadata.
   
   Related: #4021, which covers the missing check on the last attempt.
   
   ### Willingness to contribute
   
   - [x] I would be willing to contribute a fix for this bug with guidance from 
the Iceberg community
   
   I have a fix with regression tests for both this and #4021: it keys the 
landed check on the `AddSnapshotUpdate` snapshot ids that were sent, and runs 
it after every `CommitFailedException`, including on the last attempt. I'm 
happy to open a PR.
   


-- 
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]

Reply via email to