-
Notifications
You must be signed in to change notification settings - Fork 1.1k
A smidge faster unwrap non-null #4059
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
Conversation
Test Results 324 files 324 suites 3m 29s ⏱️ Results for commit 7e0a66e. ♻️ This comment has been updated with latest results. |
*/ | ||
public <T extends GraphQLOutputType> T getUnwrappedNonNullTypeAs() { | ||
return GraphQLTypeUtil.unwrapNonNullAs(this.type); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes the consuming code easier to read
assertTrue(!GraphQLTypeUtil.isNonNull(wrappedType), | ||
"A non null type cannot wrap an existing non null type '%s'", GraphQLTypeUtil.simplePrint(wrappedType)); | ||
assertTrue(!GraphQLTypeUtil.isNonNull(wrappedType), () -> | ||
String.format("A non null type cannot wrap an existing non null type '%s'", GraphQLTypeUtil.simplePrint(wrappedType))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically more efficient
* | ||
* @param type the type to unwrap | ||
* | ||
* @return the underlying type that is not {@link GraphQLNonNull} | ||
*/ | ||
public static GraphQLType unwrapNonNull(GraphQLType type) { | ||
while (isNonNull(type)) { | ||
type = unwrapOne(type); | ||
// its illegal to have a type that is a non-null wrapping a non-null type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if the input type is non-null list of non-null type? it should be handled here? IIRC I'm using this method somewhere in one project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GraphqlNonNull has code to prevent it being double wrapped.
So
field( arg : String!!)
aka
nonNull(nonNull(GraphqlString))
is actually illegal and wont ever be allowed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok that is good but what about a type like
ids: [ID!]!
?
I suspect that the while loop was there to handle that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of [ID!]!
it's not non-null wrapping a non-null, it would be non-null wrapping a list, wrapping a non-null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok but I think this could be a breaking change for some projects (and this class is not marked as @internal IIRC)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrapping a non-null with another non-null is not allowed as per the specification: in the grammar it can only wrap a named or list type, not another non-null type. https://spec.graphql.org/draft/#sec-Type-References
Is there a situation where you would have had a non-null wrapping another non-null in your experience?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about the breaking change aspect and its not there. We already had assertion code to prevent ids : ID!!
as a type so the code was overly defensive and I suspect written without the understanding that double non nulls is actually illegal.
So there is no breaking change here. Just a more efficient unwrapping of a single non null layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the problem I see is that the previous code was unwrapping all the wrapper types (because unwrapOne
/getWrappedType
work for List too):
so, previously, a client of this method could call it with [ID!]!
to get ID
. Is there a unit test for this behavior?
Of course, I could be wrong ... I'm pretty sure my code could be broken by this change but I don't have my laptop with me now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem I see is that the previous code was unwrapping all the wrapper types (because unwrapOne/getWrappedType work for List too):
Its ok - itt didnt work like that. It only every unwrapped a non null . The code was
public static GraphQLType unwrapNonNull(GraphQLType type) {
while (isNonNull(type)) {
type = unwrapOne(type);
}
public static GraphQLType unwrapOne(GraphQLType type) {
if (isNonNull(type)) {
return ((GraphQLNonNull) type).getWrappedType();
} else if (isList(type)) {
return ((GraphQLList) type).getWrappedType();
}
return type;
}
So it would check if its non null and stop once it reached a type that is not non null. The reason its faster is that unwrapOne
does another isNonNull
instanceof check inside itself. So this avoids that.
Its not a major saving but its some saving. Thats why I said its a smidge
faster
smidgen
/ˈsmɪdʒɪn/
nouninformal
noun: smidgen; plural noun: smidgens; noun: smidgin; plural noun: smidgins; noun: smidgeon; plural noun: smidgeons
a small amount of something.
"add a smidgen of cayenne"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good then, thanks for the explanation!
assertTrue(!GraphQLTypeUtil.isNonNull(wrappedType), | ||
"A non null type cannot wrap an existing non null type '%s'", GraphQLTypeUtil.simplePrint(wrappedType)); | ||
assertTrue(!GraphQLTypeUtil.isNonNull(wrappedType), () -> | ||
"A non null type cannot wrap an existing non null type"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to remove the name of the type in this error message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I did - why?
because a static value that takes in no "enclosed" variables allocates no memory - the Supplier becomes a global singleton compiled into place.
but if we "enclose" a variable then it allocates the Supplier on every call.
So in the 0.0001% case where we have an error, yes the error message is better BUT we pay a memory price on the 99.9999% of the time where there is no error.
So yes its a less informative error message, but its also better at memory usage!
#3939 shows that
graphql.schema.GraphQLTypeUtil#unwrapNonNull
is called a lotThis is a smidge faster since it does less checking of values
So
graphql.execution.ExecutionStepInfo.getUnwrappedNonNullType
callsgraphql.schema.GraphQLTypeUtil.unwrapNonNull
so we can discount it but this makes the underlying method a smidge quickerAlso we save a extra loop in the lookup because double non null wrapping cant happen and hence we can save the loop checking