9.3.3.5 App Retry Responsibility
A focused guide to App Retry Responsibility, connecting core concepts with practical Docker and container operations.
App retry responsibility refers to the application-level code that handles connection failures and retries for its own dependencies, an essential complement to Compose's startup-time depends_on coordination, since that coordination alone cannot address every scenario where a dependency is temporarily unavailable.
Why This Responsibility Falls to the Application, Not Compose
Compose's depends_on mechanism addresses startup sequencing — it has no ongoing role once services are running, meaning any later, transient unavailability of a dependency is a scenario the application itself needs to handle through its own logic.
import time
def connect_with_retry(max_attempts=5, delay=2):
for attempt in range(max_attempts):
try:
return connect_to_database()
except ConnectionError:
if attempt == max_attempts - 1:
raise
time.sleep(delay)
Designing Retry Logic With Appropriate Backoff
Retrying immediately and repeatedly against a still-unavailable dependency can itself contribute to an ongoing problem — incorporating a backoff strategy, where the delay between attempts grows, is generally a more considerate approach.
def connect_with_backoff(max_attempts=5):
delay = 1
for attempt in range(max_attempts):
try:
return connect_to_database()
except ConnectionError:
if attempt == max_attempts - 1:
raise
time.sleep(delay)
delay *= 2
Handling Both Initial Connection and Ongoing Reconnection
Retry logic is valuable not just for an application's initial startup connection attempt, but also for gracefully reconnecting if an established connection is lost at some later point during normal operation.
def get_connection():
global _connection
if _connection is None or _connection.closed:
_connection = connect_with_backoff()
return _connection
Why App Retry Responsibility Matters
Building reasonable retry and reconnection logic directly into an application is essential resilience that Compose's own startup coordination cannot provide on its own, ensuring an application can gracefully handle both its initial startup sequencing and any later, transient unavailability of its dependencies.