jaydenseric / apollo-upload-client

A terminating Apollo Link for Apollo Client that fetches a GraphQL multipart request if the GraphQL variables contain files (by default FileList, File, or Blob instances), or else fetches a regular GraphQL POST or GET request (depending on the config and GraphQL operation).
https://npm.im/apollo-upload-client
1.53k stars 155 forks source link

Apollo Client query deduplication incorrectly deduplicates similar GraphQL operations with different files in variables #351

Open jerbob92 opened 3 months ago

jerbob92 commented 3 months ago

When executing the same mutation with the same variables but with a different file in parallel, Apollo thinks that the request is the same, and will just wait for the first one to complete and returns that a response for all of them.

I have found out that setting queryDeduplication to false in Apollo (default is true), will fix this behavior. The reason for this is because with apollo-upload-client, the variables that go into Apollo are indeed exactly the same, so it thinks they are the same.

Is there anything that can be done about this? Perhaps adding a random dummy string (or a file hash) in place where the file will be mapped?

If not, it might be good to add information about this in the docs.

jaydenseric commented 1 month ago

with apollo-upload-client, the variables that go into Apollo are indeed exactly the same

I'm a bit rusty about how all this fits together, but doesn't Apollo Client receive the GraphQL operation variables before it gets to the terminating link created via createUploadLink, that makes the final fetch request? The variables Apollo Client process here for deduplication should contain the Blob, File, or FileList instances (or whatever special values you have custom defined "files" to upload as being):

https://github.com/apollographql/apollo-client/blob/3f6d023db2d7d6bf977060dd5db6291fd64e85e6/src/core/QueryManager.ts#L1113-L1133

I think the underlying problem is that when Apollo Client processes the GraphQL operation variables to check if they are the same as a currently active request, it JSON stringifies them with this function, which results in any File instances becoming {}, looking the same even if they have different contents:

https://github.com/apollographql/apollo-client/blob/3f6d023db2d7d6bf977060dd5db6291fd64e85e6/src/utilities/common/canonicalStringify.ts#L59-L62

Apollo Client is making a flawed assumption that all terminating links that make the final request do a basic JSON stringify on variables, but that is not the case for apollo-upload-client and most likely other community terminating links. To fix this, Apollo could try to detect things like File instances in the JSON stringify replacer when generating a key for cache comparison, and replace the File instance with something that uniquely identifies that specific file value (perhaps a string hash of the file's contents).

A more robust approach is to probably compare the request variables with active request variables using the unmodified request variables objects, by recursing into them and deep comparing the values so that two instances of a particular class would be identified as seperate values.

I don't have the availability to report this Apollo Client bug or pursue a fix, @jerbob92 would you like to take that up? If Apollo accepts the issue as a bug in Apollo Client to fix, we can close this issue here. If they don't, then really all we can do is document that people should turn off Apollo Client query deduplication, either for all requests (lazy approach), or via a custom Apollo Link that only turns it off if the variables contain files.