BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Four Strategies to Handle Backpressure - Jay Phelps at ReactiveConf 2019

Four Strategies to Handle Backpressure - Jay Phelps at ReactiveConf 2019

Leia em Português

Jay Phelps, RxJS core team member, recently presented at ReactiveConf 2019 in Prague and explained what backpressure is, when it happens, and which strategies can be applied to deal with it.

To illustrate what backpressure is, Phelps gave the example of a hard drive which can read data at 150 MB/s but can write data only at 100 MB/s. This leaves a read/write deficit of 50MB every second. A 6GB file is read at full speed in 40s and, if immediately written to another section of the disk, will lead to a 2GB piece of memory used to keep track of the data that was read but could not be written immediately. That memory may or may not be available. This is a typical backpressure problem, caused by a producer (the reading process) which is faster than a consumer (the writing process).

Phelps defined back pressure in a software context as a resistance or force opposing the desired flow of data. In the previous case, the mentioned resistance comes from the mitigating strategy of reading data only as fast as data can be written, thus limiting (resisting) the producer speed. In most I/O libraries (like Node.js streams), backpressure mechanisms operate under the hood, and the library user needs not concern themself with it.

Backpressure may also occur in the context of UI rendering. The producer here can be the user producing keyboard inputs, which may lead to some computations ultimately resulting in UI updates. When the stream of UI updates is slower than the stream of keyboard inputs, backpressure may be necessary. This is often achieved by throttling/debouncing keyboard input.

As Phelps mentioned in an example, updating the DOM faster than the screen updates (60fps) may be wasteful. The window.requestAnimationFrame method may help synchronizing DOM updates with the screen update cycle.

After the introductory examples, Phelps summarized the four main strategies to handle backpressure. The first strategy is to scale up resources so the consumer has the same or higher speed than the producer. The second strategy, as previously illustrated, is to control the producer. In that strategy, the consumer decides at which speed the producer sends data. Phelps provided a quick example in which the consumer pulls data from the producer instead of being pushed data:

const source = connectToSource();

source.pull(response1 => {
  console.log(response1);

  // later...
 
  source,pull(response2 => {
    console.log(response2);
  });
});

The next strategy, which was used in the aforementioned hard drive example, is buffering, i.e. accumulating incoming data spikes temporarily. Such lossless backpressure strategy may however lead to unbounded buffers, or buffers outgrowing the available memory.

Another strategy consists of allowing data loss by dropping part of the incoming data. Sampling/throttling/debouncing strategies adjust the incoming flow of data to the consumer by disregarding excess data.

Phlelps subsequently narrowed in on available libraries which may help implement backpressure strategies. Full-fledged push-based streams libraries (like Rxjs) may be used to implement lossy backpressure (Rxjs’s throttle, debounce, audit, or sample methods). However, push-based streams do not natively allow control of the producer. That means lossless backpressure requires potentially unbounded buffering. Pull-based streams (like Node.js streams, Web Streams, asynchronous iterators – cf. Repeater.js, IxJS - the pull-based version of Rxjs) may allow to control or pause the producer. Note that asynchronous iterators are inherently push-pull mechanisms, as they pull a request for data, data which when computed is pushed to the consumer.

Push and pull solutions are not exclusive and may be used together in the same application. This commonly happens server-side, but it is also true on the client. Rxjs can for instance be used, in some context, in conjunction with iterators to provide lossless backpressure without resorting to unbounded buffers.

Which strategy to adopt will depend on the problem at hand, e.g. whether it is possible, or makes sense, in a specific context to control the producer. In the case of user inputs, it is for instance not possible to scale up resources, nor to control user behaviour, while lossy backpressure strategies often help. It is beneficial to developers to understand the backpressure concept and the strategies to address it to deliver an optimal user experience.

ReactiveConf is a yearly conference targeted at developers with talks addressing the latest technologies and trends in software development. ReactiveConf 2019 took place Oct. 30 - Nov. 1, 2019, and is the fifth installment of the event.

Rate this Article

Adoption
Style

BT