Skip to content

Commit fba79ba

Browse files
stevie9868Yingjian Wukevinjqliu
authored
abort the whole table transaction if any updates in the transaction has failed (#1246)
* abort the whole transaction if any update on the chain has failed * Update tests/integration/test_writes/test_writes.py Co-authored-by: Kevin Liu <[email protected]> * Update tests/integration/test_writes/test_writes.py Co-authored-by: Kevin Liu <[email protected]> * add type:ignore to prevent lint error --------- Co-authored-by: Yingjian Wu <[email protected]> Co-authored-by: Kevin Liu <[email protected]>
1 parent 3f8cb17 commit fba79ba

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

pyiceberg/table/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from dataclasses import dataclass
2424
from functools import cached_property
2525
from itertools import chain
26+
from types import TracebackType
2627
from typing import (
2728
TYPE_CHECKING,
2829
Any,
@@ -33,6 +34,7 @@
3334
Optional,
3435
Set,
3536
Tuple,
37+
Type,
3638
TypeVar,
3739
Union,
3840
)
@@ -237,9 +239,12 @@ def __enter__(self) -> Transaction:
237239
"""Start a transaction to update the table."""
238240
return self
239241

240-
def __exit__(self, _: Any, value: Any, traceback: Any) -> None:
241-
"""Close and commit the transaction."""
242-
self.commit_transaction()
242+
def __exit__(
243+
self, exctype: Optional[Type[BaseException]], excinst: Optional[BaseException], exctb: Optional[TracebackType]
244+
) -> None:
245+
"""Close and commit the transaction if no exceptions have been raised."""
246+
if exctype is None and excinst is None and exctb is None:
247+
self.commit_transaction()
243248

244249
def _apply(self, updates: Tuple[TableUpdate, ...], requirements: Tuple[TableRequirement, ...] = ()) -> Transaction:
245250
"""Check if the requirements are met, and applies the updates to the metadata."""

tests/integration/test_writes/test_writes.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,3 +1448,27 @@ def test_rewrite_manifest_after_partition_evolution(session_catalog: Catalog) ->
14481448
EqualTo("category", "A"),
14491449
),
14501450
)
1451+
1452+
1453+
@pytest.mark.integration
1454+
@pytest.mark.parametrize("format_version", [1, 2])
1455+
def test_abort_table_transaction_on_exception(
1456+
spark: SparkSession, session_catalog: Catalog, arrow_table_with_null: pa.Table, format_version: int
1457+
) -> None:
1458+
identifier = "default.table_test_abort_table_transaction_on_exception"
1459+
tbl = _create_table(session_catalog, identifier, properties={"format-version": format_version})
1460+
1461+
# Pre-populate some data
1462+
tbl.append(arrow_table_with_null)
1463+
table_size = len(arrow_table_with_null)
1464+
assert len(tbl.scan().to_pandas()) == table_size
1465+
1466+
# try to commit a transaction that raises exception at the middle
1467+
with pytest.raises(ValueError):
1468+
with tbl.transaction() as txn:
1469+
txn.append(arrow_table_with_null)
1470+
raise ValueError
1471+
txn.append(arrow_table_with_null) # type: ignore
1472+
1473+
# Validate the transaction is aborted and no partial update is applied
1474+
assert len(tbl.scan().to_pandas()) == table_size # type: ignore

0 commit comments

Comments
 (0)