-
-
Notifications
You must be signed in to change notification settings - Fork 155
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
Support batching again #34
Comments
@jaydenseric I don't get how to use |
@Eraldo My new advice is to wait until Apollo v2 has proper support for batching. You could downgrade everything to use the old spec, but really you don't want to since the latest versions require totally different resolver code and work so much better that the old, now unsupported versions. |
Great, thank you @jaydenseric for your explanation. So, I guess I will remove batching and hope it will be re-supported soon. ;) |
Any news on batching? |
@adamjking3 yes! @evans has been busy adding batching support to After a lot of discussions it has been decided to add upload support to the official There are no plans to deprecate |
@jaydenseric Sounds exciting! So from your meeting notes |
It will be documented along with new features in |
@jaydenseric Do you have any idea as to when apollo-upload-client and/or batching will be released into an upcoming version of apollo-link-http? |
Once apollographql/apollo-link#364 is merged I will create a new PR. Because that prior PR has big changes and involves API design decisions it is not being rushed; I'm not sure when it will be out. Hopefully soon. My PR might take a few days to put together? There needs to be Apollo documentation updates etc. so a release will depend on the wider Apollo team, which is super busy at the moment. Realistically it might take from a few days, to a few weeks from when apollographql/apollo-link#364 is merged. If it is easy enough I will add batching support here in the meantime. |
An update… The Apollo PR will likely not be merged. A new PR could be weeks out, and would prioritise abstracting the fetching internals for reuse between packages over batching. One reason batching is not trivial to implement with Apollo links is that the fetch URI and options can be changed by links per operation context. So hypothetically, if there were 10 requests within a 10ms batching cycle, 5 might be headed to If I were to implement something like this here, there is a risk that when there is an official API for it in a few weeks I'll have to junk what we have here and potentially ship a breaking change. The energy would probably be better spent helping Apollo work out a generic solution. Generally speaking, interest in batching is waning as the benefits don't seem to be as great as expected. There are performance tradeoffs:
The tradeoffs are circumstantial, so I'm sure there are situations where batching might be valuable. As a side note, I'm predicting complicated edge cases when processing batched mutations with upload streams on the server. Ideally we could get to a place where we can comfortably abort uploads in a mutation resolver if there is a validation issue on one of the arguments. But what if the multipart batched request contains other mutations with files that are fine? Aborting uploads for one mutation would abort the request for all. As the resolver is ignorant if the request is batched or not, it has to assume it is never safe to abort the request. So another tradeoff to batching might be sacrificing the aborting uploads early in resolvers if there are validation issues. |
@jaydenseric that all makes sense, maybe we shouldn't be using batching after all 😊 Thank you for the update! |
Our app performance is severely degraded when batching is not available. Especially when loading a form where there are several select boxes each trying to load their choices. I think batching still has a lot of value when combined with a short wait time and a page doing seemingly unrelated things. So from my point of view, I wouldn't say that interest in batching is waning, on the contrary, it is a huge gain for a negligible cost. |
I've been following this thread for a while now, and I've disabled batching because I need file uploads via mutations. Since my project is not live yet, I can get by, but I have 5 initial calls (working on reducing these) to my GraphQL backend and with batching disabled I get the overhead of the transport layer for each of these calls - instead of just once. I don't need batching when uploading files - my users can wait for the requests to finish before continuing. That's not an issue for me. The issue I'm having right now is that I can't combine |
@Zn4rK this is very exactly our situation. I understand it will take a bit more time to solve things properly and that's fine. I am willing to wait and help when I'll have time. But i just wanted to make my voice heard, that the need for batching should not be underestimated. |
This might be a bit off topic for the batching, but I figured that my solution might help a few others in this thread. I'm sorry for my tone in my previous reply - reading it back a few weeks later, it feels a bit rude. Something like this would allow the use case I described above. const isObject = node => typeof node === 'object' && node !== null;
// rough first draft, could probably be optimised in a loads of different ways.
const hasFiles = (node, found = []) => {
Object.keys(node).forEach((key) => {
if (!isObject(node[key]) || found.length > 0) {
return;
}
if (
(typeof File !== 'undefined' && node[key] instanceof File) ||
(typeof Blob !== 'undefined' && node[key] instanceof Blob)
) {
found.push(node[key]);
return;
}
hasFiles(node[key], found);
});
return found.length > 0;
};
const httpLink = ApolloLink.split(
({ variables }) => hasFiles(variables),
createUploadLink(options),
new BatchHttpLink(options),
); I experimented with the idea of using extract-files for the detection of files, but since it actually extracts the files from the variables object I just moved on to recursively checking the object for files instead. I'm sure my detection has some issues with FileLists etc - I just left them out right now. This works great for me, since I only have uploads in mutations that users triggers. Thanks @jaydenseric for your work on this superb implementation, and the spec! |
This is probably unblocked from being worked on, now that I can't spend time on it, but someone else feel free to take a crack at it! It has the potential to be a bit complicated and/or opinionated, so I can't promise a quick turnaround for a review and merge. |
Thanks @Zn4rK for sharing your solution. I was able to re-activate batching for most queries in no time ! |
I modified @Zn4rK solution for a slightly more performant version that is working for me. I wasn't able to paste the code here, apologies I included an example of the split at bottom. I needed to remove __typename for some mutations and not for Upload etc, among other needs.
https://gist.github.com/sbrichardson/83ce25d91841bafc9365aba914007189 |
@sbrichardson thanks for sharing. The try/catch seems excessive to me. In what cases would you expect to have an error thrown when iterating JSON objects by their keys ? |
For sure @PowerKiKi, I think it can be cleaned up some, just overly cautious, since I hadn't tested it much when I shared, and while loops crash a browser so quickly. Side note -- @Zn4rK I think taking advantage of context to control this process when needed is much more performant without needing to iterate over all the variables on every request. This method is especially useful since I only have a few Upload mutations, compared to many normal mutations/queries. I've confirmed this works well. Example: /**
* Apollo Terminating link
* The split will skip Batching if an
* operation's context contains hasUpload: true.
*/
const httpLink = ApolloLink.split(
operation => operation.getContext().hasUpload,
createUploadLink(OPTS),
new BatchHttpLink(OPTS)
)
/**
* Then within a mutation component, specify the context
* with hasUpload to activate the Upload link.
*/
const { data } = await uploadMutation({
variables: { file },
context: {
hasUpload: true, // activate Upload link
}
}) |
Heej @sbrichardson, A much cleaner solution indeed. I borrowed your idea for the context variable and published an article how to do batching/uploading with Apollo & Rails: Might be interesting for people in this issue. |
I'm not sure if this is relevant or off-topic, but I managed to get batched upload working with Apollo Angular and the
This is still one mutation, and one http call. Maybe this will help someone. |
Use apollo-upload-client v13. This drops support for rewriting queries without files as GET, but will be easier to maintain. That functionality can still be supported using split. See jaydenseric/apollo-upload-client#34 for examples of using split to support batching.
Use apollo-upload-client v13. This drops support for rewriting queries without files as GET, but will be easier to maintain. That functionality can still be supported using split. See jaydenseric/apollo-upload-client#34 for examples of using split to support batching.
Use apollo-upload-client v13. This drops support for rewriting queries without files as GET, but will be easier to maintain. That functionality can still be supported using split. See jaydenseric/apollo-upload-client#34 for examples of using split to support batching.
thankyou all for the split solution Worked like a charm I only had to update the mutation hooks const [upload, { data, loading, error }] = useMutation(
mutation,
{
context: { hasUpload: true },
}
) |
Cheers @sbrichardson for the fix Theres a potential modification which gets rid of setting the context (Although it might still be better to explicitly set the context)
It looks for variable definitions of type Appears to be working for me so far. UPDATEThe above only works if the file is required. it fails to work if file is allowed to be nullable. Although |
Note that const { extractFiles } = require('extract-files'); const { files } = extractFiles(variables);
const hasFiles = !!files.size |
@jaydenseric provides a much better solution import { extractFiles } from 'extract-files';
const uploadAndBatchHTTPLink = opts => ApolloLink.split(
operation => extractFiles(operation).files.size > 0,
createUploadLink(opts),
new BatchHttpLink(opts)
); It also has the advantage that if your setting a file to None, it can still be batched. @jaydenseric would you consider re-exporting extractFiles, or exporting a helper function, for this purpose? It seems like this would be a common issue and even though "extract-files" is used by "apollo-upload-client", the correct thing would be to still add it to your "package.json" as its called within the application code. Would be nicer if we didn't have to do that. |
I don’t plan to implement support for batching at this time, please refer to the earlier discussion in this issue:
|
Support for batching has been temporarily dropped in the rush to support the final Apollo Client v2 API (see #33 (comment)).
I am waiting on
apollo-link-batch-http
to be updated (it is still usingapollo-fetch
) so I can refer to it when writing our new batching terminating link.The text was updated successfully, but these errors were encountered: