2525from typing import TYPE_CHECKING , Annotated , Any , Dict , Generic , List , Literal , Optional , Tuple , TypeVar , Union , cast
2626
2727from pydantic import Field , field_validator , model_validator
28- from tenacity import RetryCallState , before_sleep_log , retry , retry_if_exception_type , stop_after_attempt , wait_random_exponential
28+ from tenacity import (
29+ RetryCallState ,
30+ before_sleep_log ,
31+ retry ,
32+ retry_if_exception_type ,
33+ stop_after_attempt ,
34+ stop_after_delay ,
35+ wait_random_exponential ,
36+ )
2937
3038from pyiceberg .exceptions import CommitFailedException
3139from pyiceberg .partitioning import PARTITION_FIELD_ID_START , PartitionSpec
3745 COMMIT_MIN_RETRY_WAIT_MS_DEFAULT ,
3846 COMMIT_NUM_RETRIES ,
3947 COMMIT_NUM_RETRIES_DEFAULT ,
48+ COMMIT_RETRY_TOTAL_TIMEOUT_MS ,
49+ COMMIT_RETRY_TOTAL_TIMEOUT_MS_DEFAULT ,
4050 SUPPORTED_TABLE_FORMAT_VERSION ,
4151 TableMetadata ,
4252 TableMetadataUtil ,
@@ -86,6 +96,9 @@ def commit(self) -> None:
8696 self ._transaction .table_metadata .properties .get (COMMIT_MAX_RETRY_WAIT_MS , COMMIT_MAX_RETRY_WAIT_MS_DEFAULT )
8797 )
8898 num_retries = int (self ._transaction .table_metadata .properties .get (COMMIT_NUM_RETRIES , COMMIT_NUM_RETRIES_DEFAULT ))
99+ timeout_ms = int (
100+ self ._transaction .table_metadata .properties .get (COMMIT_RETRY_TOTAL_TIMEOUT_MS , COMMIT_RETRY_TOTAL_TIMEOUT_MS_DEFAULT )
101+ )
89102
90103 def _before_commit_inner (state : RetryCallState ) -> None :
91104 if state .attempt_number > 1 :
@@ -94,7 +107,7 @@ def _before_commit_inner(state: RetryCallState) -> None:
94107 @wraps (self .commit )
95108 @retry (
96109 wait = wait_random_exponential (min = min_wait_ms / 1000 , max = max_wait_ms / 1000 ),
97- stop = stop_after_attempt (num_retries ),
110+ stop = ( stop_after_attempt (num_retries ) | stop_after_delay ( timeout_ms / 1000 ) ),
98111 retry = retry_if_exception_type (CommitFailedException ),
99112 before = _before_commit_inner ,
100113 before_sleep = lambda state : logger .debug (
0 commit comments