Resilience
Relay provides resilience patterns to handle transient failures gracefully.
Retry Configuration
Section titled “Retry Configuration”Basic Retry
Section titled “Basic Retry”use Cline\Relay\Support\Attributes\Resilience\Retry;
#[Get]#[Retry(times: 3)]class GetDataRequest extends Request{ public function endpoint(): string { return '/data'; }}Retry with Delay
Section titled “Retry with Delay”#[Retry(times: 3, delay: 1000)] // 1 second between retriesclass GetDataRequest extends Request {}Exponential Backoff
Section titled “Exponential Backoff”#[Retry(times: 3, delay: 100, multiplier: 2.0, maxDelay: 30000)]// Retry 1: wait 100ms// Retry 2: wait 200ms// Retry 3: wait 400msclass GetDataRequest extends Request {}Retry on Specific Status Codes
Section titled “Retry on Specific Status Codes”#[Retry(times: 3, delay: 500, when: [429, 500, 502, 503, 504])]class GetDataRequest extends Request {}Custom Retry Condition
Section titled “Custom Retry Condition”#[Retry(times: 3, callback: 'shouldRetry')]class GetDataRequest extends Request{ public function shouldRetry(Response $response, int $attempt): bool { if ($response->serverError()) { return true; } $errorCode = $response->json('error.code'); return in_array($errorCode, ['TEMPORARY_ERROR', 'SERVICE_BUSY']); }}Timeout Configuration
Section titled “Timeout Configuration”Request Timeout
Section titled “Request Timeout”use Cline\Relay\Support\Attributes\Resilience\Timeout;
#[Get]#[Timeout(seconds: 5)]class QuickCheckRequest extends Request {}Connection and Request Timeout
Section titled “Connection and Request Timeout”#[Timeout(seconds: 30, connectSeconds: 5)]class SlowEndpointRequest extends Request {}Connector-Level Timeout
Section titled “Connector-Level Timeout”class MyConnector extends Connector{ public function timeout(): int { return 30; }
public function connectTimeout(): int { return 10; }}Circuit Breaker
Section titled “Circuit Breaker”Prevents repeated calls to a failing service.
Basic Circuit Breaker
Section titled “Basic Circuit Breaker”use Cline\Relay\Support\Attributes\Resilience\CircuitBreaker;
#[Get]#[CircuitBreaker( failureThreshold: 5, resetTimeout: 30,)]class UnreliableApiRequest extends Request {}Circuit Breaker with Success Threshold
Section titled “Circuit Breaker with Success Threshold”#[CircuitBreaker( failureThreshold: 5, resetTimeout: 30, successThreshold: 3, // Require 3 successes to close)]class UnreliableApiRequest extends Request {}Circuit Breaker States
Section titled “Circuit Breaker States”- Closed - Normal operation, requests flow through
- Open - Requests fail immediately without calling the API
- Half-Open - Limited requests allowed to test if service recovered
Circuit Breaker Policy
Section titled “Circuit Breaker Policy”use Cline\Relay\Support\Contracts\CircuitBreakerPolicy;
class ApiCircuitBreakerPolicy implements CircuitBreakerPolicy{ public function failureThreshold(): int { return 5; } public function resetTimeout(): int { return 30; } public function successThreshold(): int { return 2; }
public function isFailure(Request $request, Response $response): bool { return $response->serverError(); }
public function onOpen(string $key): void { logger()->warning("Circuit opened: {$key}"); }}
#[CircuitBreaker(policy: ApiCircuitBreakerPolicy::class)]class ApiRequest extends Request {}Combining Resilience Patterns
Section titled “Combining Resilience Patterns”#[Get]#[Timeout(seconds: 10)]#[Retry(times: 3, sleepMs: 500, multiplier: 2, when: [500, 502, 503])]#[CircuitBreaker(failureThreshold: 5, resetTimeout: 60)]class ResilientRequest extends Request {}Execution order:
- Timeout - Each attempt has a 10-second limit
- Retry - If timeout or 5xx error, retry up to 3 times with backoff
- Circuit Breaker - If 5 consecutive failures, open circuit
Error Handling Patterns
Section titled “Error Handling Patterns”Graceful Degradation
Section titled “Graceful Degradation”try { $response = $connector->send(new GetProductsRequest()); cache()->put('products', $response->json(), 3600); return $response->json();} catch (RequestException $e) { if ($cached = cache()->get('products')) { return $cached; } return ['products' => [], 'error' => 'Service temporarily unavailable'];}Bulkhead Pattern
Section titled “Bulkhead Pattern”Isolate failures by using separate connectors:
class PaymentConnector extends Connector{ public function rateLimit(): ?RateLimitConfig { return new RateLimitConfig(maxAttempts: 100, decaySeconds: 60); }}
class InventoryConnector extends Connector{ // Independent from payment service failures public function rateLimit(): ?RateLimitConfig { return new RateLimitConfig(maxAttempts: 500, decaySeconds: 60); }}Best Practices
Section titled “Best Practices”- Combine patterns - Timeout + Retry + Circuit Breaker
- Use exponential backoff - Avoid thundering herd
- Set appropriate thresholds - Balance availability and protection
- Log circuit state changes - Monitor service health