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
Memoize type.getFields in ObjectTypeComposer.js? #319
Comments
Wow, it sounds very bad. Let's try to investigate what's wrong. I start to add my thought in the free form here. Maybe we can found the root of the problem together. Step 1
So the problem somewhere higher - something calls this method many times. Step 2If we take a look at simplified getType(): GraphQLObjectType { // <------------------------------------------------ POINT_0
this._gqType.astNode = getObjectTypeDefinitionNode(this);
this._gqType._fields = () => {
return defineFieldMap( // <---------------------------------------------------- POINT_1
this._gqType,
mapEachKey(this._gqcFields, (fc, name) => this.getFieldConfig(name)), // <-- POINT_2
this._gqType.astNode
);
};
this._gqType._interfaces = () => this.getInterfacesTypes();
return this._gqType;
}
Definitely, we need to add memorization for |
Now is the good question: How we can add memorization to cc @vladar maybe you have any thoughts? |
EDIT: reading #319 (comment) again I get it now — CPU sampling is only capturing where time is being spent not that actual stack trace so yeah, memoizing I think
|
How do you change the type? Are they immutable? Or have easily trackable ways of mutation? If so, we could just always return the cached version right? |
Or perhaps if it's not easy to tell when a type is mutated, perhaps we could manually "freeze" the types since we're controlling changing them. |
For me this code path looks problematic: graphql-compose/src/ObjectTypeComposer.js Lines 965 to 974 in e863711
Anytime we call So getFields(): GraphQLFieldMap<any, any> {
if (typeof this._fields === 'function') {
this._fields = this._fields();
}
return this._fields;
} So maybe if we skip this code path when |
Probably a bad idea - 53 tests are failing 😅 EDIT: actually only 2 tests are failing with this change: getType(): GraphQLObjectType {
this._gqType.astNode = getObjectTypeDefinitionNode(this);
if (graphqlVersion >= 14) {
if (typeof this._gqType._fields !== `object`) {
this._gqType._fields = () =>
defineFieldMap(
this._gqType,
mapEachKey(this._gqcFields, (fc, name) => this.getFieldConfig(name)),
this._gqType.astNode
);
} |
I am actually not sure what would be the correct fix for it. With my suggestion, you won't be able to override type fields (and args) after the schema was built. I think the good fix requires a way to "freeze" types explicitly (and maybe freeze them automatically once the schema is built). When frozen - it will simply return the But the fix for general case is probably more involved 😒 |
@vladar made exactly what I suppose to do to fix this problem 👍👍👍 @KyleAMathews can you make profiling again from the gatsby master branch and provide new timings? |
About asked questions and suggestions:
Nope, types are not immutable. And it's a good feature request but a solution will be very complicated - need to track
For now, there are no trackable ways of mutation. Your issue is the first request in this field. So I'm very glad that we found this problem. But I'm sorry that this blind point brought inconvenience.
Yep, we definitely should freeze types when calling This feature will be highly demanded in graphql-compose-modules in the future. BTW take some time to look at this new way of schema construction. I have been using it for more than a year and very happy with it. It uses directory structure for schema definition (like next.js uses files in the
No. It's a bad idea for the following reasons:
|
Profiled the same site again with @vladar's fix and time spent in graphql-compose dropped from 1578ms to 5ms 😆 so that's good change. QPS went from 349 to 1527 so 4.4x speedup! |
Great news! 🎉 I'm curious. Do you create graphql schema for every request? |
I considered different scenarios how to fix this problem and chose the following plan:
Sorry, but these changes may take several months. I am happy that @vladar's fix works fine for you. This gives me time to fix this problem properly and remove old hacky things. @vladar, if you have any thoughts or additions, please tell me ;) |
Hey @nodkz Thank you so much for your prompt replies 💜 The plan sounds cool to me! I am not sure about extensions though - we use them interchangeably with directives. But I think we will figure something out. In general, separating the two makes sense to do (as for me). |
🎉 This issue has been resolved in version 9.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
@vladar now all You may upgrade to v9.0.0 and remove the following hotfix gatsbyjs/gatsby#29822 |
Great news, thanks! 🎉 |
I'm doing some profiling on Gatsby query running for a site & the hottest function is getting the fields for a type. I assume this could be memoized? It gets called 10s of thousands of times for a small-ish site.
The text was updated successfully, but these errors were encountered: