Architecture Basics
A resilient AWS-backed service is not one big retry loop bolted onto the end of your code.
Busque em todas as páginas da documentação
A resilient AWS-backed service is not one big retry loop bolted onto the end of your code.
It is a small set of habits applied consistently at the boundary between your code and each AWS dependency: a timeout so a stuck call cannot hang forever, structured logging so a failure is diagnosable, and a single place per dependency where that logic lives instead of being copied at every call site. This page builds that minimal wrapper in both SDKs. Later pages in this section turn the same wrapper into a circuit breaker, a bulkhead, and an idempotent operation.
pip install boto3 (boto3 1.43.x, Python 3.10+). TypeScript: npm install @aws-sdk/client-dynamodb (Node.js 18+), or whichever per-service client you are wrapping.One small function per downstream dependency, applying a timeout and structured logging around any call.
# --- Python (boto3) ---
import logging
import time
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeout
logger = logging.getLogger("aws.dynamodb")
_executor = ThreadPoolExecutor(max_workers=4) # bounded pool per dependency
def call_with_timeout(fn, *args, timeout_s=3, **kwargs):
"""Run fn(*args, **kwargs) with a hard timeout and structured logging."""
start = time.monotonic()
future = _executor.submit(fn, *args, **kwargs)
try:
result = future.result(timeout=timeout_s)
logger.info("call ok", extra={"elapsed_ms": (time.monotonic() - start) * 1000})
return result
except FutureTimeout:
logger.warning("call timed out", extra={"timeout_s": timeout_s})
raise
except Exception:
logger.exception("call failed")
raise// --- TypeScript (AWS SDK v3) ---
type AsyncFn<T> = () => Promise<T>;
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error("call timed out")), timeoutMs)),
]);
}
export async function callWithTimeout<T>(fn: AsyncFn<T>, timeoutMs = 3000): Promise<T> {
const start = Date.now();
try {
const result = await withTimeout(fn(), timeoutMs);
console.log(JSON.stringify({ level: "info", msg: "call ok", elapsedMs: Date.now() - start }));
return result;
} catch (err) {
console.warn(JSON.stringify({ level: "warn", msg: "call failed", error: String(err) }));
throw err;
}
}print - is what lets you trace a failure back to a specific dependency and call.Applying the wrapper to an actual DynamoDB read, so the timeout and logging are real, not theoretical.
# --- Python (boto3) ---
import boto3
ddb = boto3.client("dynamodb", region_name="us-east-1")
def get_order(order_id: str):
return call_with_timeout(
ddb.get_item,
TableName="orders",
Key={"id": {"S": order_id}},
timeout_s=2,
)
item = get_order("order-42")// --- TypeScript (AWS SDK v3) ---
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
const ddb = new DynamoDBClient({ region: "us-east-1" });
async function getOrder(orderId: string) {
return callWithTimeout(
() => ddb.send(new GetItemCommand({
TableName: "orders",
Key: { id: { S: orderId } },
})),
2000,
);
}
const item = await getOrder("order-42");get_order / getOrder still returns the same shape the SDK would.Giving DynamoDB and S3 their own wrapper state keeps a slow S3 call from starving DynamoDB calls of pool capacity - the seed of a bulkhead.
# --- Python (boto3) ---
from concurrent.futures import ThreadPoolExecutor
# Separate bounded pools per dependency - not one shared pool for everything.
dynamodb_pool = ThreadPoolExecutor(max_workers=4)
s3_pool = ThreadPoolExecutor(max_workers=4)// --- TypeScript (AWS SDK v3) ---
// Node's event loop is single-threaded, so the "pool" here is a concurrency
// limiter per dependency rather than a thread pool - same isolation goal.
class ConcurrencyLimiter {
private active = 0;
private queue: (() => void)[] = [];
constructor(private readonly max: number) {}
async run<T>(fn: () => Promise<T>): Promise<T> {
if (this.active >= this.max) {
await new Promise<void>((resolve) => this.queue.push(resolve));
}
this.active++;
try {
return await fn();
} finally {
this.active--;
this.queue.shift()?.();
}
}
}
const dynamoDbLimiter = new ConcurrencyLimiter(4);
const s3Limiter = new ConcurrencyLimiter(4);Related: Designing for AWS's Failure Modes - the failure shapes this wrapper is designed against.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última atualização: 23 de jul. de 2026