Skip to content
On this page

Introduction of concurrency

This ADR is effective starting from v0.12.

TL;DR

To apply concurrency settings to the Query or Mutation use concurrency operator:

ts
import { concurrency } from '@farfetched/core';

concurrency(operation, { strategy: 'TAKE_LATEST' });

To abort all in-flight requests use abortAll Event:

ts
import { createEvent } from 'effector';
import { concurrency } from '@farfetched/core';

const abortAll = createEvent();

concurrency(operation, { abortAll });

By default, no concurrency settings are applied.

Field concurrency in factories createJsonQuery and createJsonMutation will be deleted.

Configuration vs. Operators

This change is about global movement of Farfetched's API from configuration to operators. Configuration of operation during its creation has a couple of drawbacks:

  • It makes creation of custom factories in user-land harder, because they have to support all possible configurations and their combinations. On the other hand, operators are easier to support because they are just functions that can be called with any operation.

  • It makes code-generation harder, because it requires to generate the whole configuration object in generated code and user does not have a chance to modify it. On the other hand, operators can be called in separate file and its calls would not be affected by code-generation.

The last but not least, operator is more Effector-ish way of doing things, because any operator is just a set of sample-s which is foundation of Effector.

For example, let us take a look at the following code:

ts
retry(query, {
  times: 12,
});

Basically, it can be rewritten as:

ts
const nextRetry = createEvent();
const $retriesNotExceededLimit = createStore(false);

sample({
  clock: query.finished.failed,
  filter: $retriesNotExceededLimit,
  target: [query.start, nextRetry],
});

sample({
  clock: nextRetry,
  source: $retryNumber,
  fn: (retryNumber) => retryNumber <= 12,
  target: $retriesNotExceededLimit,
});

TIP

It is not a real implementation of retry operator, but it is a good example of how it can be implemented using Effector primitives.

So, Farfetched's API is moving from configuration to operators (retry, cache, timeout, etc.), and this ADR is about moving concurrency settings to operator as well.

Future of concurrency

Historical context

Historically, concurrency settings were applied directly to the Query or Mutation using createJsonQuery and createJsonMutation factories and concurrency field:

ts
import { createJsonQuery } from '@farfetched/core';

const query = createJsonQuery({
  concurrency: { strategy: 'TAKE_FIRST' },
});

Furthermore, concurrency.strategy has a default value TAKE_LATEST which means that by default, all in-flight requests are aborted except the latest one. But only Queries created by createJsonQuery had this default, any other operations had TAKE_EVERY as a default.

This behavior is considered harmful and after introducing the concurrency operator, default concurrency strategy is switched to TAKE_EVERY for all use-case which means that all in-flight requests are executed concurrently.

Configuration to operator

Field concurrency in createJsonQuery and createJsonMutation is deprecated. Instead, use concurrency operator.

strategy is mapped as is:

ts
import { createJsonQuery, concurrency } from '@farfetched/core';

const query = createJsonQuery({
  concurrency: { strategy: 'TAKE_LATEST' }, 
});

concurrency(query, { strategy: 'TAKE_LATEST' }); 

abort is renamed to abortAll:

ts
import { createJsonQuery, concurrency } from '@farfetched/core';
import { createEvent } from 'effector';

const abortQuery = createEvent();

const query = createJsonQuery({
  concurrency: { abort: abortQuery }, 
});

concurrency(query, { abortAll: abortQuery }); 

Furthermore, default concurrency strategy is switched to TAKE_EVERY. Before, the default strategy was TAKE_LATEST in createJsonQuery. Now, all operations have TAKE_EVERY as a default which means that all in-flight requests are executed concurrently.

Changes schedule

Since this change requires a change in the user code, it is scheduled for the couple of releases:

v0.12

v0.13

  • createJsonQuery requires applying concurrency operator on then to specify concurrency settings. Otherwise, a deprecation message is written with console.warning. The reason for this is to make sure that the user is aware of the change of default behavior.

v0.14

Released under the MIT License.