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

Race conditions when invalidations come in quick succession #3105

Closed
GeorchW opened this issue Jan 20, 2023 · 2 comments
Closed

Race conditions when invalidations come in quick succession #3105

GeorchW opened this issue Jan 20, 2023 · 2 comments
Milestone

Comments

@GeorchW
Copy link
Contributor

GeorchW commented Jan 20, 2023

There is a simple race condition in the library right now:

  1. A query that provides a tag starts.
  2. A mutation invalidates the same tag.
  3. The query finishes.

The query is not rerun in this case, despite its tags having been invalidated. I actually noticed this in my application when the query took a bit too long while mutations were applied.

The problem stated here is similar to the problem stated in #2203, but the title/framing is different in this issue.

A simple fix could be to add the option to not invalidate any tags while any queries (or mutations) are running, and only invalidate them after all pending queries are settled. This would also solve #2203. It could, however, cause problems for people using extremely long-running queries (since no tags are invalidated while they run), so it might make sense to allow disabling this behavior.

Implementation-wise, we could add another field to the API slice named pendingTagInvalidations, which contains a list of tags that will be invalidated once all queries are settled. Each time that a mutation settles, it checks if there are any queries/mutations that are in the pending state. If that is the case, it adds the tags to pendingTagInvalidations, else it invalidates them directly. When a query settles, it does the same check and invalidates all tags in pendingTagInvalidations, then clears the list.

Pseudocode:

// api is our API slice
function hasPendingRequests(api: ApiSlice) {
    const hasPendingQueries = Object.values(queries)
        .some(query => query?.status === QueryStatus.pending)
    const hasPendingMutations = Object.values(mutations)
        .some(mutation => mutation?.status === QueryStatus.pending);
    return hasPendingQueries || hasPendingMutations;
}
// When a mutation settles:
if (hasPendingQueries(api)) {
    api.pendingTagInvalidations.push(...invalidatedTags);
} else {
    invalidateTags(invalidatedTags);
}
// When a query settles:
if (!hasPendingQueries(api)) {
    invalidateTags(api.pendingTagInvalidations);
    api.pendingTagInvalidations = [];
}

The overhead of this shouldn't be too high (in O(#endpoints) ), but if required, we could save the list of pending requests separately (making it O(1)).

I'd be happy to contribute!

@markerikson
Copy link
Collaborator

Yeah, we're definitely open to PRs if you think you have a solution!

@markerikson
Copy link
Collaborator

@markerikson markerikson added this to the 2.0 milestone Oct 28, 2023
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

No branches or pull requests

2 participants