Batch Insert Pattern
Buffer database inserts and flush in batches for dramatically faster data loading.
Automatically retry failed operations with increasing delays and jitter to prevent thundering herd problems.
function retryWithBackoff(
callable $operation,
int $maxAttempts = 3,
int $baseDelayMs = 100
): mixed {
$lastException = null;
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
try {
return $operation();
} catch (Throwable $e) {
$lastException = $e;
if ($attempt === $maxAttempts) {
break;
}
// Exponential backoff: 100ms, 200ms, 400ms...
$delayMs = $baseDelayMs * (2 ** ($attempt - 1));
// Add jitter (±25%) to prevent thundering herd
$jitter = $delayMs * 0.25;
$delayMs += random_int((int) -$jitter, (int) $jitter);
usleep($delayMs * 1000);
}
}
throw $lastException;
}
Let us trace what happens when an API call fails twice then succeeds on the third attempt:
Attempt 1: Call API → Fails (timeout)
→ Wait ~100ms (base delay)
Attempt 2: Call API → Fails (server error)
→ Wait ~200ms (doubled)
Attempt 3: Call API → Succeeds
→ Return result immediately
Total wait: ~300ms
Without retry: immediate failure, manual intervention needed
The jitter is critical. Without it, if 100 clients all fail at the same time, they all retry at the same time — overwhelming the server again. Jitter spreads the retries randomly across a time window, giving the server breathing room to recover.
Without retry:
API hiccup → Pipeline fails → Manual restart → Data delayed
With retry + backoff:
API hiccup → Auto-retry in 100ms → Succeeds → Pipeline continues
Transient failures become invisible to the user
// Retry an API call up to 3 times with exponential backoff
$response = retryWithBackoff(
fn() => $httpClient->post('/api/data', $payload),
maxAttempts: 3,
baseDelayMs: 200
);
// Retry a database connection
$pdo = retryWithBackoff(
fn() => new PDO($dsn, $user, $pass),
maxAttempts: 5,
baseDelayMs: 500
);
Use this for any operation that can fail transiently: API calls, database connections, file locks, external service requests. Do not use it for operations that fail deterministically — retrying bad input three times will not make it valid.