Commit 6ec572f3 authored by Eugenia Grieff's avatar Eugenia Grieff Committed by Mark Chao

GraphQL - Preload epics group using Lookahead

parent 6d08bd46
...@@ -18,6 +18,7 @@ module EE ...@@ -18,6 +18,7 @@ module EE
field :epics, ::Types::EpicType.connection_type, null: true, field :epics, ::Types::EpicType.connection_type, null: true,
description: 'Find epics', description: 'Find epics',
extras: [:lookahead],
max_page_size: 2000, max_page_size: 2000,
resolver: ::Resolvers::EpicsResolver resolver: ::Resolvers::EpicsResolver
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Resolvers module Resolvers
class EpicsResolver < BaseResolver class EpicsResolver < BaseResolver
include TimeFrameArguments include TimeFrameArguments
include LooksAhead
argument :iid, GraphQL::ID_TYPE, argument :iid, GraphQL::ID_TYPE,
required: false, required: false,
...@@ -49,7 +50,7 @@ module Resolvers ...@@ -49,7 +50,7 @@ module Resolvers
super(args) super(args)
end end
def resolve(**args) def resolve_with_lookahead(**args)
@resolver_object = object.respond_to?(:sync) ? object.sync : object @resolver_object = object.respond_to?(:sync) ? object.sync : object
return [] unless resolver_object.present? return [] unless resolver_object.present?
...@@ -62,8 +63,12 @@ module Resolvers ...@@ -62,8 +63,12 @@ module Resolvers
attr_reader :resolver_object attr_reader :resolver_object
def unconditional_includes
[:group]
end
def find_epics(args) def find_epics(args)
EpicsFinder.new(context[:current_user], args).execute apply_lookahead(EpicsFinder.new(context[:current_user], args).execute)
end end
def epic_feature_enabled? def epic_feature_enabled?
...@@ -72,7 +77,7 @@ module Resolvers ...@@ -72,7 +77,7 @@ module Resolvers
def transform_args(args) def transform_args(args)
transformed = args.dup transformed = args.dup
transformed[:group_id] = group.id transformed[:group_id] = group
transformed[:parent_id] = parent.id if parent transformed[:parent_id] = parent.id if parent
transformed[:iids] ||= [args[:iid]].compact transformed[:iids] ||= [args[:iid]].compact
......
...@@ -30,8 +30,7 @@ module Types ...@@ -30,8 +30,7 @@ module Types
description: 'Indicates if the epic is confidential' description: 'Indicates if the epic is confidential'
field :group, GroupType, null: false, field :group, GroupType, null: false,
description: 'Group to which the epic belongs', description: 'Group to which the epic belongs'
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.group_id).find }
field :parent, EpicType, null: true, field :parent, EpicType, null: true,
description: 'Parent epic of the epic', description: 'Parent epic of the epic',
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Epic, obj.parent_id).find } resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Epic, obj.parent_id).find }
......
---
title: Preload epics in GraphQL group queries using Lookahead
merge_request: 42874
author:
type: performance
...@@ -196,5 +196,42 @@ RSpec.describe 'getting group information' do ...@@ -196,5 +196,42 @@ RSpec.describe 'getting group information' do
expect(graphql_errors).to eq([nil, nil]) expect(graphql_errors).to eq([nil, nil])
end end
end end
context 'when loading multiple epics' do
let_it_be(:group) { create(:group) }
before do
stub_licensed_features(epics: true)
query_epics(1)
end
it 'can lookahead to eliminate N+1 queries', :use_clean_rails_memory_store_caching do
create_list(:epic, 10, group: group)
group.reload
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
query_epics(1)
end.count
expect { query_epics(10) }.not_to exceed_all_query_limit(control_count)
end
end
def query_epics(number)
epics_field = <<~NODE
epics(first: #{number}) {
edges {
node {
title
}
}
}
NODE
post_graphql(
graphql_query_for('group', { 'fullPath' => group.full_path }, epics_field),
current_user: user
)
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment