Hands-on Learning: Exponential Backoff in Action

Exponential Backoff is a retry strategy used in computing and network systems to manage transient errors by gradually increasing the wait time between retry attempts. This approach helps prevent overwhelming the server with a high volume of immediate retry requests and increases the chances of success by spacing out the retries.

Key Characteristics of Exponential Backoff:

  • Incremental Delays: The time between each retry attempt increases exponentially.
  • Randomization: Often, a small random jitter is added to the wait time to avoid collisions when multiple clients are retrying simultaneously.
  • Maximum Attempts: There is typically a cap on the maximum number of retry attempts to prevent indefinite retries.

Implementation in the Example

In the provided example below, we demonstrate exponential backoff using a FastAPI server to simulate Server unavaibility and a Python client retrying to get a successful response from the clinet. Here’s how it is implemented:

FastAPI Server (server.py):

  • Simulated Service Unavailability: The server is designed to fail the first 10 requests with a 503 Service Unavailable error to simulate a transient error scenario.
  • Success after Failures: After 10 failures, the server starts responding successfully to illustrate the retry mechanism eventually succeeding.

from fastapi import FastAPI, HTTPException

app = FastAPI()
failed_attempts = 0
threshold_failures = 10

@app.get("/process")
async def process_request():
global failed_attempts
if failed_attempts < threshold_failures:
failed_attempts += 1
raise HTTPException(status_code=503, detail="Service Unavailable")
return {"message": "Request processed successfully"}

Python Client (client.py):

  • Retry Mechanism: The client uses the tenacity library to implement exponential backoff. The @retry decorator is configured to handle retries with increasing delays.
  • Detailed Logging: Each retry attempt logs the initial delay, retry number, time between retries, and the reason for retries, providing insights into the backoff process.
  • Exponential Delay Configuration: The delay starts at 1 second and increases exponentially, with a maximum wait time of 60 seconds and up to 10 retry attempts.
import httpx
import logging
from tenacity import retry, stop_after_attempt, wait_exponential, before_sleep_log, after_log

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@retry(
wait=wait_exponential(multiplier=1, min=1, max=60),
stop=stop_after_attempt(10),
before_sleep=before_sleep_log(logger, logging.INFO),
after=after_log(logger, logging.INFO)
)
async def call_api():
async with httpx.AsyncClient() as client:
response = await client.get("http://127.0.0.1:8000/process")
response.raise_for_status()
return response.json()

async def main():
try:
result = await call_api()
logger.info(f"API call succeeded: {result}")
except httpx.HTTPStatusError as exc:
logger.error(f"API call failed: {exc.response.status_code} {exc.response.text}")

if __name__ == "__main__":
import asyncio
asyncio.run(main())

This example showcases how exponential backoff can effectively manage transient errors, ensuring that the client retries requests intelligently without overwhelming the server, and ultimately succeeding when the server becomes available.

Leave a Reply

Your email address will not be published. Required fields are marked *