Commit cf1b0d10 authored by charlieablett's avatar charlieablett

Address reviewer comments

- Add 1 for all fields that call Gitaly (with resolvers or without)
- Clarify comment regarding Gitaly call alert
- Expose predicate `calls_gitaly?` instead of ivar
parent a11fe5de
...@@ -4,8 +4,6 @@ module Types ...@@ -4,8 +4,6 @@ module Types
class BaseField < GraphQL::Schema::Field class BaseField < GraphQL::Schema::Field
prepend Gitlab::Graphql::Authorize prepend Gitlab::Graphql::Authorize
attr_reader :calls_gitaly
DEFAULT_COMPLEXITY = 1 DEFAULT_COMPLEXITY = 1
def initialize(*args, **kwargs, &block) def initialize(*args, **kwargs, &block)
...@@ -17,19 +15,12 @@ module Types ...@@ -17,19 +15,12 @@ module Types
def base_complexity def base_complexity
complexity = DEFAULT_COMPLEXITY complexity = DEFAULT_COMPLEXITY
complexity += 1 if @calls_gitaly complexity += 1 if calls_gitaly?
complexity complexity
end end
def calls_gitaly_check(calls) def calls_gitaly?
return if @calls_gitaly @calls_gitaly
return if calls < 1
# Will inform you if :calls_gitaly should be true or false based on the number of Gitaly calls
# involved with the request.
raise "Gitaly is called for field '#{name}' #{"on type #{owner.name} " if owner}- please add `calls_gitaly: true` to the field declaration"
rescue => e
Gitlab::Sentry.track_exception(e)
end end
private private
...@@ -51,6 +42,7 @@ module Types ...@@ -51,6 +42,7 @@ module Types
proc do |ctx, args, child_complexity| proc do |ctx, args, child_complexity|
# Resolvers may add extra complexity depending on used arguments # Resolvers may add extra complexity depending on used arguments
complexity = child_complexity + self.resolver&.try(:resolver_complexity, args, child_complexity: child_complexity).to_i complexity = child_complexity + self.resolver&.try(:resolver_complexity, args, child_complexity: child_complexity).to_i
complexity += 1 if calls_gitaly?
field_defn = to_graphql field_defn = to_graphql
......
...@@ -7,7 +7,7 @@ module Gitlab ...@@ -7,7 +7,7 @@ module Gitlab
# Check if any `calls_gitaly: true` declarations need to be added # Check if any `calls_gitaly: true` declarations need to be added
def instrument(_type, field) def instrument(_type, field)
type_object = field.metadata[:type_class] type_object = field.metadata[:type_class]
return field unless type_object && type_object.respond_to?(:calls_gitaly_check) return field unless type_object && type_object.respond_to?(:calls_gitaly?)
old_resolver_proc = field.resolve_proc old_resolver_proc = field.resolve_proc
...@@ -15,16 +15,24 @@ module Gitlab ...@@ -15,16 +15,24 @@ module Gitlab
previous_gitaly_call_count = Gitlab::GitalyClient.get_request_count previous_gitaly_call_count = Gitlab::GitalyClient.get_request_count
result = old_resolver_proc.call(typed_object, args, ctx) result = old_resolver_proc.call(typed_object, args, ctx)
current_gitaly_call_count = Gitlab::GitalyClient.get_request_count current_gitaly_call_count = Gitlab::GitalyClient.get_request_count
type_object.calls_gitaly_check(current_gitaly_call_count - previous_gitaly_call_count) calls_gitaly_check(type_object, current_gitaly_call_count - previous_gitaly_call_count)
result result
rescue => e
ap "#{e.message}"
end end
field.redefine do field.redefine do
resolve(gitaly_wrapped_resolve) resolve(gitaly_wrapped_resolve)
end end
end end
def calls_gitaly_check(type_object, calls)
return if type_object.calls_gitaly?
return if calls < 1
# Will inform you if there needs to be `calls_gitaly: true` as a kwarg in the field declaration
# if there is at least 1 Gitaly call involved with the field resolution.
error = RuntimeError.new("Gitaly is called for field '#{type_object.name}' on #{type_object.owner.try(:name)} - please add `calls_gitaly: true` to the field declaration")
Gitlab::Sentry.track_exception(error)
end
end end
end end
end end
......
...@@ -74,9 +74,11 @@ describe Types::BaseField do ...@@ -74,9 +74,11 @@ describe Types::BaseField do
context 'calls_gitaly' do context 'calls_gitaly' do
context 'for fields with a resolver' do context 'for fields with a resolver' do
it 'adds 1 if true' do it 'adds 1 if true' do
field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true) with_gitaly_field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: true, calls_gitaly: true)
without_gitaly_field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: true)
base_result = without_gitaly_field.to_graphql.complexity.call({}, {}, 2)
expect(field.to_graphql.complexity).to eq 2 expect(with_gitaly_field.to_graphql.complexity.call({}, {}, 2)).to eq base_result + 1
end end
end end
...@@ -100,26 +102,5 @@ describe Types::BaseField do ...@@ -100,26 +102,5 @@ describe Types::BaseField do
expect(field.to_graphql.complexity).to eq 12 expect(field.to_graphql.complexity).to eq 12
end end
end end
describe '#calls_gitaly_check' do
let(:gitaly_field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true) }
let(:no_gitaly_field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: false) }
context 'if there are no Gitaly calls' do
it 'does not raise an error if calls_gitaly is false' do
expect { no_gitaly_field.send(:calls_gitaly_check, 0) }.not_to raise_error
end
end
context 'if there is at least 1 Gitaly call' do
it 'does not raise an error if calls_gitaly is true' do
expect { gitaly_field.send(:calls_gitaly_check, 1) }.not_to raise_error
end
it 'raises an error if calls_gitaly: is false or not defined' do
expect { no_gitaly_field.send(:calls_gitaly_check, 1) }.to raise_error(/please add `calls_gitaly: true`/)
end
end
end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Graphql::CallsGitaly::Instrumentation do
subject { described_class.new }
context 'when considering complexity' do
describe '#calls_gitaly_check' do
let(:gitaly_field) { Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true) }
let(:no_gitaly_field) { Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: false) }
context 'if there are no Gitaly calls' do
it 'does not raise an error if calls_gitaly is false' do
expect { subject.send(:calls_gitaly_check, no_gitaly_field, 0) }.not_to raise_error
end
end
context 'if there is at least 1 Gitaly call' do
it 'does not raise an error if calls_gitaly is true' do
expect { subject.send(:calls_gitaly_check, gitaly_field, 1) }.not_to raise_error
end
it 'raises an error if calls_gitaly: is false or not defined' do
expect { subject.send(:calls_gitaly_check, no_gitaly_field, 1) }.to raise_error(/please add `calls_gitaly: true`/)
end
end
end
end
end
...@@ -137,7 +137,7 @@ describe 'GraphQL' do ...@@ -137,7 +137,7 @@ describe 'GraphQL' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:query) do let(:query) do
graphql_query_for('project', { 'fullPath' => project.full_path }, %w(forksCount)) graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id))
end end
before do before do
......
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