Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Retry Strategies for Remote Services

Retry Strategies for Remote Services

Various strategies for retrying failed remote service requests

Avatar for Chris Trombley

Chris Trombley

April 27, 2018

Other Decks in Technology

Transcript

  1. Services that exist in different processes or nodes seperated by

    a network. • HTTP APIs • Microservices • Application Servers • Remote databases
  2. One of the big differences between in-memory calls and remote

    calls is that remote calls can fail, or hang without a response until some timeout limit is reached. -- Martin Fowler
  3. One of the big differences between in-memory calls and remote

    calls is that remote calls can fail, or hang without a response until some timeout limit is reached. -- Martin Fowler Remote services can experience failures due to their dependency on a network.
  4. Retryable Errors • Transient failures • Hardware failures • Logical

    bugs • Resource contention • Rate limits • Quotas • Throttles
  5. Retry With Backoff • Transient failures • Resource contention •

    Short term quotas • Throttles • Timeouts
  6. Basic Implementation $attempt = 1; do { $backoff = $this->getBackoff();

    sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  7. Implementation $attempt = 1; $constant = 1; do { sleep($constant);

    $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  8. Constant Retry Strategy Attempt Backoff Cumulative Backoff 1 1 1

    2 1 2 3 1 3 4 1 4 5 1 5 6 1 6 7 1 7 8 1 8 9 1 9 10 1 10 Mean Delay: 1s
  9. Implementation $delay = 1; $attempt = 1; $constant = 1;

    do { $delay += $constant; sleep($delay); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  10. Linear Retry Strategy Attempt Backoff Cumulative Backoff 1 2 2

    2 3 5 3 4 9 4 5 14 5 6 20 6 7 27 7 8 35 8 9 44 9 10 54 10 11 65 Mean Delay: 6.5s
  11. Implementation $base = 1; $attempt = 1; $factor = 3;

    do { $delay = $base * pow($attempt, $factor); sleep($delay); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  12. Polynomial Backoff Strategy Attempt Backoff Cumulative Backoff 1 1 1

    2 8 9 3 27 36 4 64 100 5 125 225 6 216 441 7 343 784 8 512 1296 9 729 2025 10 1000 3025 Mean Delay: 302.5s
  13. Implementation $base = 1; $attempt = 1; do { $delay

    = $base * pow(2, $attempt); sleep($delay); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  14. Exponential Backoff Strategy Attempt Backoff Cumulative Backoff 1 2 2

    2 4 6 3 8 14 4 16 30 5 32 62 6 64 126 7 128 254 8 256 510 9 512 1022 10 1024 2046 Mean Delay: 204.6s
  15. Exponential Backoff Strategy Attempt Backoff Cumulative Backoff 1 2 2

    2 4 6 3 8 14 4 16 30 5 32 62 6 64 126 7 128 254 8 256 510 9 512 1022 10 1024 (~17 min) 2046 (~34 min) Mean Delay: 204.6s
  16. Common Limits for Retry Strategies • Retry attempts: To prevent

    a request from being retried indefinitely
  17. Common Limits for Retry Strategies • Retry attempts: To prevent

    a request from being retried indefinitely • Backoff duration: To keep the maximum delay within a reasonable timeframe
  18. Implementation 1. Set a max limit for retry attempts 2.

    When limit is reached • Raise an error • Implement an error handling strategy
  19. Limiting Retry Attempts $attempt = 1; $maxAttempts = 10; do

    { if ($attempt > $maxAttempts) { throw new \Exception(); } $backoff = $this->getDelay(); sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  20. Implementation 1. Set a maximum duration of the backoff 2.

    When limit is reached • Raise an error • Continue to retry with the maximum backoff value until the maximum attempts are reached
  21. Limiting Backoff Duration $attempt = 1; $maxAttempts = 10; $maxBackoffDuration

    = 64; do { if ($attempt > $maxAttempts) { throw new \Exception(); } $backoff = min($maxBackoffDuration, $this->getDelay()); sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  22. Exponential Backoff Strategy Attempt Backoff Cumulative Backoff 1 2 2

    2 4 6 3 8 14 4 16 30 5 32 62 6 64 126 7 128 254 8 256 510 9 512 1022 10 1024 (~17 min) 2046 (~34 min) Backoff Mean Delay: 204.6s
  23. Exponential Backoff Strategy Attempt Backoff Cumulative Backoff Lim. Backoff Lim.

    Cumulative 1 2 2 2 2 2 4 6 4 6 3 8 14 8 14 4 16 30 16 30 5 32 62 32 62 6 64 126 64 126 7 128 254 64 190 8 256 510 64 254 9 512 1022 64 318 10 1024 2046 (~34 min) 64 382 (~6.5 min) Backoff Mean Delay: 204.6s Lim. Backoff Mean Delay: 38.2s
  24. When the limit is reached • Raise an error and

    stop retrying • Implement an error handling strategy
  25. When the limit is reached • Raise an error and

    stop retrying • Implement an error handling strategy • Inform a circuit breaker pattern of change in state
  26. The data we've been examining up to this point assumes

    we have one client connection per period.
  27. Implementation $base = 1; $attempt = 1; $maxAttempts = 10;

    $maxBackoffDuration = 64; do { if ($attempt > $maxAttempts) { throw new \Exception(); } $constant = rand(1, 1000) * .001; $backoff = min($maxDuration, $base * pow(2, $attempt)) + $constant; sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  28. Constant Jitter Attempt Backoff Cumulative Backoff 1 2.252 2.252 2

    4.111 6.363 3 8.957 15.32 4 16.091 31.411 5 32.848 64.259 6 64.785 129.044 7 64.635 193.679 8 64.24 257.919 9 64.668 322.587 10 64.247 386.834 Mean Delay: 38.6s
  29. Implementation $base = 1; $attempt = 1; $maxAttempts = 10;

    $maxBackoffDuration = 64; do { if ($attempt > $maxAttempts) { throw new \Exception(); } $backoff = min($maxDuration, $base * pow(2, $attempt)); $backoff = $backoff / 2 + rand(0, $backoff / 2); sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  30. Equal Jitter Attempt Backoff Cumulative Backoff 1 1 1 2

    2 3 3 4 7 4 14 21 5 26 47 6 35 82 7 53 135 8 41 176 9 58 234 10 62 296 Mean Delay: 29.6s
  31. Full Jitter $base = 1; $attempt = 1; $maxAttempts =

    10; $maxBackoffDuration = 64; do { if ($attempt > $maxAttempts) { throw new \Exception(); } $backoff = min($maxDuration, $base * pow(2, $attempt)); $backoff = rand(0, $backoff); sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  32. Full Jitter Attempt Backoff Cumulative Backoff 1 1 1 2

    2 3 3 8 11 4 13 24 5 11 35 6 18 53 7 21 74 8 15 89 9 35 124 10 23 147 Mean Delay: 14.7s
  33. Decorrelated Jitter $base = 1; $attempt = 1; $maxAttempts =

    10; $maxBackoffDuration = 64; do { if ($attempt > $maxAttempts) { throw new \Exception(); } $backoff = min($maxDuration rand($base, $previousBackoff * 3)); $backoff = rand($base, $backoff); $previousBackoff = $backoff; sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  34. Decorrelated Jitter $base = 1; $attempt = 1; $maxAttempts =

    10; $maxBackoffDuration = 64; do { if ($attempt > $maxAttempts) { throw new \Exception(); } $backoff = min($maxDuration rand($base, $previousBackoff * 3)); $backoff = rand($base, $backoff); $previousBackoff = $backoff; sleep($backoff); $response = $this->doRequest(); $attempt++; } while ($response->hasFailed());
  35. Decorrelated Jitter Attempt Backoff Cumulative Backoff 1 3 3 2

    1 4 3 2 6 4 3 9 5 6 15 6 7 22 7 3 25 8 4 29 9 1 30 10 3 33 Mean Delay: 3.3s
  36. • What is the cost to connect • Will the

    service be available quickly
  37. • What is the cost to connect • Will the

    service be available quickly • How quickly do I need a response
  38. Other Considerations • Read The Docs • Would retrying create

    an inconsistant state? • Quotas, Rate limits, Throttles?
  39. Other Considerations • Read The Docs • Will retrying create

    an inconsistant state? • Quotas, Rate limits, Throttles?
  40. Will retrying create an inconsistant state? Read Requests • Generally

    no risk in retries • Set sensible defaults for $base, $maxAttempts and $maxBackoffDuration • Retry as needed
  41. Will retrying create an inconsistant state? Destructive Requests • Create,

    Update, Delete • Determine if the request is Idempotent • Retry as needed
  42. Safe and Idempotent REST Methods Method Safe Idempotent GET yes

    yes POST no no PUT no yes PATCH no no DELETE no yes
  43. Other Considerations • Read The Docs • Would retrying create

    an inconsistant state? • Quotas, Rate limits, Throttles?
  44. Quotas and Rate Limits • Determine a strategy for the

    use case • Set sensible defaults for $base, $maxAttempts and $maxBackoffDuration • Retry as Needed
  45. Other Considerations • Read The Docs • Would retrying create

    an inconsistant state? • Quotas, Rate limits, Throttles?
  46. Wrap Up • Retry strategies can be very effective in

    maintaining availability of a service with service dependencies
  47. Wrap Up • Retry strategies can be very effective in

    maintaining availability of a service with service dependencies • Limits should be implemented to avoid indefinite retries and long delays between requests.
  48. Wrap Up • Retry strategies can be very effective in

    maintaining availability of a service with service dependencies • Limits should be implemented to avoid indefinite retries and long delays between requests. • Adding Jitter to retry strategies can help avoid collisions with other clients.