Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): run getSources promises in a concurrent-safe way #347

Merged
merged 8 commits into from
Nov 19, 2020

Conversation

francoischalifour
Copy link
Member

Description

This fixes an issue where promises coming from getSources or getSuggestions resolve out of call order.

+----------------------------------+
|        100ms                     | 
| run(1) +--->  R1                 |
|        300ms                     |
| run(2) +-------------> R2 (SKIP) |
|        200ms                     |
| run(3) +--------> R3             |
+----------------------------------+

In the scenario above, although R2 is the result resolving last, we shouldn't update the results because R3 is more fresh (although it resolved earlier).

Related


cc @redox @Shipow

@codesandbox-ci
Copy link

codesandbox-ci bot commented Oct 21, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit bf6a07f:

Sandbox Source
@algolia/js-example Configuration

Copy link
Contributor

@redox redox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great! I like how clear you made (and tested) this. I'll let the team figure out whether it's mergeable.

Promise.all(
sources.filter(Boolean).map((source) => {
return Promise.resolve(normalizeSource<TItem>(source));
return runConcurrentSafePromiseForSource(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, don't we need to have a difference instance of createConcurrentSafePromise per source here? For each source, we want to keep the last result, not the last result of all the sources.
Let me know if I'm misunderstanding!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly. This case is complex because of the dynamic nature of sources. This PR doesn't cover this case anymore.

Copy link
Contributor

@Haroenv Haroenv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to look even more in detail to the core usage of this, but the util lgtm

@francoischalifour
Copy link
Member Author

The last commits make sure that the concurrency fix works at the getSources level.

It's however not fixed for getItems because we can't keep a same reference to create the wrapper on. This is a limitation that I propose we tackle in another PR.

Copy link
Contributor

@eunjae-lee eunjae-lee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. The concurrency test is super clear.

// The last item must be the one corresponding to the last query.
expect(itemsHistory[itemsHistory.length - 1]).toEqual(
expect.objectContaining({ label: 'abc' })
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a test to check for the presence of { label: 'a' } as well as the first item?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a snapshot of all the labels could make sense?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A snapshot will be fragile because there's no certainty of how many times onStateChange is called. It's called whenever a setter is called (e.g. setIsOpen, etc.), and a setter can be called user-land. It means that if we refactor something, this test will become a false negative.

I'll check for the presence of a though.

@francoischalifour francoischalifour changed the title fix(core): run promises in a concurrent-safe way fix(core): run getSources promises in a concurrent-safe way Nov 19, 2020
@francoischalifour francoischalifour merged commit 5c6ab2e into next Nov 19, 2020
@francoischalifour francoischalifour deleted the fix/core-concurrent-safe-promise branch November 19, 2020 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants