How does useLazyQuery handle concurrent queries

I have an issue with useLazyQuery.
I am executing a lazy query each time I get a subscription update. The idea is, that the subscription returns only a small part of the data, namely: id and stage. Whenever a new message arrives (because the stage has advanced PENDING->PREPARING->APPLYING->DONE), I want to query the entire object (using the id) to get a lot of data about it. My expectation is that every time I execute the query, the stage of the query’s result will be at least as advanced as the stage from the subscription.

However, the logs show that the lazy query sometimes returns a stage that’s less advanced compared to the subscription’s stage. For example, I have a log that shows the following course of events, all happening really fast:

  1. Backend publishing subscription update for PENDING (id=x)
  2. Backend publishing subscription update for PREPARING (id=x)
  3. Backend publishing subscription update for APPLYING (id=x)
    (The frontend is supposed to query the backend for each one of these updates, see the code below)
  4. Backend answers the frontend’s lazy query (result: APPLYING)
  5. Backend publishing subscription update for DONE (id=x)
  6. Frontend got a query result saying APPLYING for the subscription message of PENDING
  7. Frontend got a query result saying APPLYING for the subscription message of PREPARING
  8. Frontend got a query result saying APPLYING for the subscription message of APPLYING
    (No logs for the backend answering any query between items 5 and 9 - this is weird!)
  9. Frontend got a query result saying APPLYING for the subscription message of DONE (meaning, a log that says: subscriptionStage=DONE, queryStage=APPLYING) - this is bad!
  10. Backend answers the frontend’s lazy query a few more times (result: DONE) - might be related to other later flows

How come the query’s result is APPLYING for a subscription of DONE?
And, how come the backend does not get any query after the DONE subscription? The frontend is supposed to issue a query after each subscription update.

This is a simplified version of my code:

export const useStatus = variables => {
  const [executeQuery, result] = useLazyQuery(STATUS_DETAILED_QUERY, { fetchPolicy: 'no-cache' })
  const { data: subscriptionData } = useSubscription(STATUS_SUBSCRIPTION, variables, { fetchPolicy: 'no-cache' })
  const { id: subscriptionId, stage: subscriptionStage } = subscriptionData?.status ?? {}

  useEffect(() => {
    const effect = async () => {
      if (!subscriptionId || !subscriptionStage) {
        return
      }

      const { data: queryData } = await executeQuery({
        fetchPolicy: 'no-cache',
        variables: { id: subscriptionId },
      })

      logging.info('Updating status', { subscriptionId, subscriptionStage, queryData })
    }

    effect().catch(error => { /* Log error */ })
  }, [subscriptionId, subscriptionStage, executeQuery])

  return result.data
}

Thank you

I think I found out the reason.
It is queryDeduplication.
Apollo sees that there is already a query in flight, so it doesn’t actually execute another one, instead it just returns the same value for both executions.
To turn it off apparently I can give context to the query and inside of it give the value of { queryDeduplication: false }.

2 Likes