Commit ddb8bd90 authored by Mark Chao's avatar Mark Chao

Merge branch '210553-improve-performance-of-graphql-groupepics-query' into 'master'

GraphQL - Preload epics group using Lookahead

See merge request gitlab-org/gitlab!42874
parents 9900e6fa 6ec572f3
......@@ -18,6 +18,7 @@ module EE
field :epics, ::Types::EpicType.connection_type, null: true,
description: 'Find epics',
extras: [:lookahead],
max_page_size: 2000,
resolver: ::Resolvers::EpicsResolver
......
......@@ -3,6 +3,7 @@
module Resolvers
class EpicsResolver < BaseResolver
include TimeFrameArguments
include LooksAhead
argument :iid, GraphQL::ID_TYPE,
required: false,
......@@ -49,7 +50,7 @@ module Resolvers
super(args)
end
def resolve(**args)
def resolve_with_lookahead(**args)
@resolver_object = object.respond_to?(:sync) ? object.sync : object
return [] unless resolver_object.present?
......@@ -62,8 +63,12 @@ module Resolvers
attr_reader :resolver_object
def unconditional_includes
[:group]
end
def find_epics(args)
EpicsFinder.new(context[:current_user], args).execute
apply_lookahead(EpicsFinder.new(context[:current_user], args).execute)
end
def epic_feature_enabled?
......@@ -72,7 +77,7 @@ module Resolvers
def transform_args(args)
transformed = args.dup
transformed[:group_id] = group.id
transformed[:group_id] = group
transformed[:parent_id] = parent.id if parent
transformed[:iids] ||= [args[:iid]].compact
......
......@@ -30,8 +30,7 @@ module Types
description: 'Indicates if the epic is confidential'
field :group, GroupType, null: false,
description: 'Group to which the epic belongs',
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.group_id).find }
description: 'Group to which the epic belongs'
field :parent, EpicType, null: true,
description: 'Parent epic of the epic',
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
expect(graphql_errors).to eq([nil, nil])
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
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