Skip to content

Commit 0f53ccb

Browse files
committed
Improve Strategy
1 parent ec9d4e2 commit 0f53ccb

File tree

1 file changed

+20
-20
lines changed
  • modules/generic/testcontainers/generic

1 file changed

+20
-20
lines changed

modules/generic/testcontainers/generic/sql.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,49 @@
1616
ADDITIONAL_TRANSIENT_ERRORS.append(DBAPIError)
1717
except ImportError:
1818
logger.debug("SQLAlchemy not available, skipping DBAPIError handling")
19+
SQL_TRANSIENT_EXCEPTIONS = (TimeoutError, ConnectionError, *ADDITIONAL_TRANSIENT_ERRORS)
1920

2021

21-
class DatabaseConnectionWaitStrategy(WaitStrategy):
22+
class ExceptionsWaitStrategy(WaitStrategy):
2223
"""
23-
Wait strategy for database connection readiness using SqlContainer._connect().
24+
Generic wait strategy that retries a callable until it succeeds or times out.
2425
25-
This strategy implements retry logic and calls SqlContainer._connect()
26-
repeatedly until it succeeds or times out.
26+
This strategy can be used with any container method that needs retry logic
27+
for handling transient errors. It calls the provided callable repeatedly
28+
until it succeeds or the timeout is reached.
2729
"""
2830

29-
def __init__(self, sql_container: "SqlContainer"):
31+
def __init__(self, callable_func: callable, transient_exceptions: Optional[tuple] = None):
3032
super().__init__()
31-
self.sql_container = sql_container
33+
self.callable_func = callable_func
34+
self.transient_exceptions = transient_exceptions or (TimeoutError, ConnectionError)
3235

3336
def wait_until_ready(self, container: WaitStrategyTarget) -> None:
3437
"""
35-
Test database connectivity with retry logic by calling SqlContainer._connect().
38+
Execute the callable with retry logic until it succeeds or times out.
3639
3740
Raises:
38-
TimeoutError: If connection fails after timeout
39-
Exception: Any non-transient errors from _connect()
41+
TimeoutError: If callable fails after timeout
42+
Exception: Any non-transient errors from the callable
4043
"""
4144
import time
4245

4346
start_time = time.time()
4447

45-
transient_exceptions = (TimeoutError, ConnectionError, *ADDITIONAL_TRANSIENT_ERRORS)
46-
4748
while True:
4849
if time.time() - start_time > self._startup_timeout:
4950
raise TimeoutError(
50-
f"Database connection failed after {self._startup_timeout}s timeout. "
51-
f"Hint: Check if the database container is ready and accessible."
51+
f"Callable failed after {self._startup_timeout}s timeout. "
52+
f"Hint: Check if the container is ready and the operation can succeed."
5253
)
5354

5455
try:
55-
self.sql_container._connect()
56+
self.callable_func()
5657
return
57-
except transient_exceptions as e:
58-
logger.debug(f"Database connection attempt failed: {e}, retrying in {self._poll_interval}s...")
58+
except self.transient_exceptions as e:
59+
logger.debug(f"Callable attempt failed: {e}, retrying in {self._poll_interval}s...")
5960
except Exception as e:
60-
logger.error(f"Database connection test failed with non-transient error: {e}")
61+
logger.error(f"Callable failed with non-transient error: {e}")
6162
raise
6263

6364
time.sleep(self._poll_interval)
@@ -69,7 +70,7 @@ class SqlContainer(DockerContainer):
6970
7071
This class can serve as a base for database-specific container implementations.
7172
It provides connection management, URL construction, and basic lifecycle methods.
72-
Database connection readiness is automatically handled by DatabaseConnectionWaitStrategy.
73+
Database connection readiness is automatically handled by ExceptionsWaitStrategy.
7374
"""
7475

7576
def _connect(self) -> None:
@@ -183,8 +184,7 @@ def start(self) -> "SqlContainer":
183184

184185
try:
185186
self._configure()
186-
# Set up database connection wait strategy before starting
187-
self.waiting_for(DatabaseConnectionWaitStrategy(self))
187+
self.waiting_for(ExceptionsWaitStrategy(self._connect, SQL_TRANSIENT_EXCEPTIONS))
188188
super().start()
189189
self._transfer_seed()
190190
logger.info("Database container started successfully")

0 commit comments

Comments
 (0)