apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.37k stars 2.66k forks source link

race condition for typePolicies causing non deterministic calls of merge #9436

Open jantimon opened 2 years ago

jantimon commented 2 years ago

We use the a simple query in a components which is rendered multiple times on the page.

const QUERY = gql`
  query AllFilms {
    allFilms {
      films {
        title
      }
    }
  }
`;

const StarWars = () => {
  const { data } = useQuery(QUERY);
  return (
    <div style={{ border: "1px solid red", margin: 10 }}>
      {data &&
        data.allFilms.films.map((film, i) => (
          <div key={film.title + i}>{i}. {film.title}</div>
        ))}
    </div>
  );
};

If the component is rendered only once everything is fine: once

If the component is rendered only once and later a second time also everything is fine. apollo-lazy

In both cases the typePolicies merge function is called once after the request finishes: merge(undefined, [{ title: 'A new Hope' }])

However if the component is rendered twice concurrently something strange is happening: Concurrent render

The typePolicies merge function is called twice after the request finishes: merge(undefined, [{ title: 'A new Hope' }]) merge([{ title: 'A new Hope' }], [{ title: 'A new Hope' }])

The expected behaviour would be that only the first call is executed.

This unexpected behaviour will only occur if a second useQuery is executed while a network request for the very same query is already running.

See the full demo here: https://codesandbox.io/s/star-wars-forked-0q2r1n?file=/src/index.js

Versions

@apollo/client: 3.5.9

jantimon commented 2 years ago

We assume that the typePolicies queue up even for the same query if the network request is still running:

Excalidraw Excalidraw Excalidraw
gastonmorixe commented 2 years ago

dealing with this too

bondom commented 2 years ago

I have the same issue for request that is sent using fetchMore: fetchMore func is called twice - 2nd time when 1st call is still in progress with the same params, so requst deduplication takes place. But merge func in fieldPolicy is triggered twice, that causes broken UI

gastonmorixe commented 2 years ago

@bondom this may help Issue #9502 and PR #9503