Power of RxJS when using exponential backoff

Alex Okrushko
Jun 5, 2018 · 5 min read
Image for post
Image for post
Image for post
Image for post

AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

Most of the modern-day Angular web apps make Ajax requests to the servers. These requests involve multiple network components (such as routers, switches, etc) as well as servers’ state and everything has to go just right for them to succeed. However, sometimes it doesn’t.

To handle such circumstances web apps typically implement retry logic that retries requests until they go through or until the maximum number of requests is reached. In most cases, simple retries are good enough to achieve the goal, but sometimes more advanced approach is needed.

Exponential backoff is an algorithm that uses exponentially longer delays between retries. In this article, I’ll dive deeper into two custom RxJS operators (both are part of backoff-rxjs package) that use exponential backoff and the use cases they cover:

  • retryBackoff, operator that retries on errors
  • intervalBackoff, operator that emits sequential numbers

I’ve used the term exponential a few times already, but what does it mean? In mathematics, it’s a function of the following form:

Image for post
Image for post

In our case, as new values are emitted (x in the function above) the longer the delay between them will be. In code it translates into the following method:

With iterations starting from 0 and provided initial interval of 1000 milliseconds the emitted values would be 1000, 2000, 4000, 8000…

With that out of our way, let’s get into our first use case.

1. retryBackoff

One of the most frequent use cases for exponential backoff is to retry on error. A good example would be Google Cloud Storage (GCS) which requires this strategy to be used when retrying failed requests.

Before working on backoff-rxjs I found a few examples of exponential backoff retries in some gists or this stackoverflow answer, but none were flexible enough for my needs; thus I created retryBackoff.

retryBackoff takes either a number as initial delay or aRetryBackoffConfig for more configurations. RxJS uses marble diagrams to visualize how operator works, so here is one for our operator.

Image for post
Image for post

Notice how retryBackoff here behaves similarly to retry operator and can be as simple as:

When more customization is required retryBackoff operator takes RetryBackoffConfig that has the following shape:

If, for example, we want to limit the number of retries up to twelve, our call would look like this:

Let’s look into the properties of RetryBackoffConfig

  • initialInterval — initial delay that is also used to calculate all the rest of the delays; this is the only require property
  • maxRetries — the maximum number of retries
  • maxInterval — the maximum delay between retries
  • shouldRetry — function that lets you analyze the error and decide whether to continue retrying (return true) or stop retrying (return false)
  • backoffDelay — function for calculating custom delays

The last two functions (shouldRetry and backoffDelay) I think deserve a bit more info.

Sometimes when we get onto particular error we would like to stop retrying, for example if the return status is 404, there is little chance that it will ever succeeds.

By default delays will be doubled between each interval, however sometimes smoother backoff is needed. Through backoffDelay property we can provide a custom delay calculation function, e.g.:

backoffDelay: (iteration, initialInterval) => Math.pow(1.5, iteration) * initialInterval ,
or even slower increase of the delays
backoffDelay: (iteration, initialInterval) => Math.pow(1.1, iteration) * initialInterval

Image for post
Image for post
blue: y = 2^x, red: y = 1.5^x, green: y = 1.1^x

The example of the full app can be found at StackBlitz.

2. intervalBackoff

Have you ever wondered what your app is doing while you are sleeping? Among many tabs that are kept open is it still working hard querying your servers, using precious resources?

The second use case of exponential backoff is to reduce the frequency of the requests by exponentially increasing each delay between requests. This is handy technique may be applied when the app detects that there is no user activity — for example, no mouse movement.

Let’s take a look at the following piece of code.

Now let’s break it down what’s happening here:

  • mousemove event is tracked on the document and use it as an indicator of user activity
  • It is triggered very frequently when the mouse is moved, so we use sampleTime as a filter of those events
  • sampleTime emits the first value only once the time specified expires. If we need to make the first call immediately (and in most cases we need it) then startWith helps us to do that
  • now we are at the intervalBackoff, which is a pipeable operator that works similar to interval, however, instead of using the same delay between emissions it doubles the delay after each one
  • Once intervalBackoff emits the value we do the service call

Note, that every time the mousemove event is detected it resets the intervalBackoff.

Here is the marble diagram for intervalBackoff:

Image for post
Image for post

Similarly to the retryBackoff, intervalBackoff is also configurable and can take more than just initial delay.

Example of the app using intervalBackoff:

Summary

Exponential backoff is a very useful strategy and has at least two common use cases: interval backoff and retry backoff. backoff-rxjs package provides pipeable operators for both cases, and they are just a combination of existing RxJS operators.

Sources: https://github.com/alex-okrushko/backoff-rxjs

Special thanks to Ben Lesh, Max NgWizard K and Nicholas Jamieson for reviewing the article/operators and providing valuable feedback.

I’m also very curious about readers’ feedback (maybe there is another use case for exponential backoff that I didn’t cover?), or if you have questions or comments please ask them 👇

If you want to chat more, you can find me on twitter @AlexOkrushko. My DMs are open.

Are you interested in NgRx? Do you want to learn everything from the basics to advanced techniques?
If you are in San Francisco / Bay Area, I’ll be doing the popular 2-day NgRx workshop on October 23–24, 2019. Tickets and more info is available here: https://www.eventbrite.ca/e/ngrx-in-san-francisco-from-start-to-advanced-concepts-in-2-days-tickets-74313759455?aff=aid
Let me help you take your state management skills to the new heights! Hope to see you there!

Angular In Depth

The place where advanced Angular concepts are explained

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store