[GraphQL] Lookahead optimizations for merge requests
What does this MR do?
This MR is a working proof of concept for lookahead optimizations in GraphQL resolvers, addressing N + 1 performance issues. Tests indicate significant ~performance gains, with reductions in query counts in the tested query below from 47 to 20, a nearly 60% reduction in query count. Much higher reductions would be anticipated in typical queries in real life scenarios:
query($fullpath: String!) {
project(fullPath: $fullpath) {
mergeRequests(first: 10) {
nodes {
assignees { nodes { username } }
headPipeline { status }
}
}
}
}
This eliminates N+1 problems in this case.
This is not an automatic transformation - if applied thoroughly it would require looking over all our resolvers and types and finding opportunities for such improvements. We could automate the search provide candidates if needed.
How does this work
This MR adds a new GraphQL resolver concern: LooksAhead
, which helps with applying lookahead preloading optimisations.
This requires that the resolver include LooksAhead
, and then make use of with_lookahead
and apply_lookahead
, providing unconditional_includes
and preloads
:
Example:
class MyResolver < BaseResolver
include LooksAhead
# Rather than defining `resolve(**args)`, we implement: `resolve_with_lookahead(**args)`
def resolve_with_lookahead(**args)
apply_lookahead(MyThingFinder.new(current_user).execute)
end
# We list things that should always be preloaded:
# For example, if child_attribute is always needed (during authorization
# perhaps), then we can include it here.
def unconditional_includes
[:child_attribute]
end
# We list things that should be included if a certain field is selected:
def preloads
{
field_one: [:other_attribute],
field_two: [{ nested: [:included_attribute] }]
}
end
end
The final thing that is needed is that every field that uses this resolver needs to advertise the need for lookahead:
field :my_things, MyThingType.connection_type, null: true,
extras: [:lookahead], # Necessary
description: 'Find my things'
apply_lookahead
uses the lookahead functionality in the framework to
ActiveRecord::Relation#preload
the required associations as needed.
Screenshots
Does this MR meet the acceptance criteria?
Conformity
-
Changelog entry -
Documentation (if required) -
Code review guidelines -
Merge request performance guidelines -
Style guides -
Database guides -
Separation of EE specific content
Availability and Testing
This requires tests to verify performance impact, and that correctness is preserved.
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process.