Skip to content

Commit 6d1e5fa

Browse files
authored
Merge pull request #5586 from rmosolgo/cache-visibility-profile-lookups
Cache field and type lookups in Visibility::Profile
2 parents 89eafc9 + 85f0404 commit 6d1e5fa

2 files changed

Lines changed: 81 additions & 49 deletions

File tree

benchmark/run.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ def self.run(task)
4040
x.report("validate - abstract fragments 2") { CARD_SCHEMA.validate(ABSTRACT_FRAGMENTS_2) }
4141
x.report("validate - big query") { BIG_SCHEMA.validate(BIG_QUERY) }
4242
x.report("validate - fields will merge") { FIELDS_WILL_MERGE_SCHEMA.validate(FIELDS_WILL_MERGE_QUERY) }
43+
when "validate_profile"
44+
profile_schemas = [CARD_SCHEMA, BIG_SCHEMA, FIELDS_WILL_MERGE_SCHEMA].map do |s|
45+
ps = Class.new(s)
46+
ps.use(GraphQL::Schema::Visibility, profiles: { default: {} })
47+
ps.validate(GraphQL.parse("{ __typename }"), context: { visibility_profile: :default })
48+
ps
49+
end
50+
ctx = { visibility_profile: :default }
51+
x.report("validate (visibility profile) - introspection ") { profile_schemas[0].validate(DOCUMENT, context: ctx) }
52+
x.report("validate (visibility profile) - abstract fragments") { profile_schemas[0].validate(ABSTRACT_FRAGMENTS, context: ctx) }
53+
x.report("validate (visibility profile) - abstract fragments 2") { profile_schemas[0].validate(ABSTRACT_FRAGMENTS_2, context: ctx) }
54+
x.report("validate (visibility profile) - big query") { profile_schemas[1].validate(BIG_QUERY, context: ctx) }
55+
x.report("validate (visibility profile) - fields will merge") { profile_schemas[2].validate(FIELDS_WILL_MERGE_QUERY, context: ctx) }
4356
when "scan"
4457
require "graphql/c_parser"
4558
x.report("scan c - introspection") { GraphQL.scan_with_c(QUERY_STRING) }

lib/graphql/schema/visibility/profile.rb

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ def freeze
5454
@cached_fields.default_proc = nil
5555
@cached_arguments.default_proc = nil
5656
@loadable_possible_types.default_proc = nil
57+
@cached_field_result.default_proc = nil
58+
@cached_field_result.each { |_, h| h.default_proc = nil }
59+
@cached_type_result.default_proc = nil
5760
super
5861
end
5962

@@ -122,6 +125,14 @@ def initialize(name: nil, context:, schema:, visibility:)
122125
end.compare_by_identity
123126

124127
@loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
128+
129+
# Combined cache for field(owner, field_name) — avoids repeated kind check + parent lookup + visibility check
130+
@cached_field_result = Hash.new { |h, owner|
131+
h[owner] = Hash.new { |h2, field_name| h2[field_name] = compute_field(owner, field_name) }
132+
}.compare_by_identity
133+
134+
# Cache for type(type_name) — avoids repeated get_type + visibility + referenced? checks
135+
@cached_type_result = Hash.new { |h, type_name| h[type_name] = compute_type(type_name) }
125136
end
126137

127138
def field_on_visible_interface?(field, owner)
@@ -149,58 +160,11 @@ def field_on_visible_interface?(field, owner)
149160
end
150161

151162
def type(type_name)
152-
t = @visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
153-
if t
154-
if t.is_a?(Array)
155-
vis_t = nil
156-
t.each do |t_defn|
157-
if @cached_visible[t_defn] && referenced?(t_defn)
158-
if vis_t.nil?
159-
vis_t = t_defn
160-
else
161-
raise_duplicate_definition(vis_t, t_defn)
162-
end
163-
end
164-
end
165-
vis_t
166-
else
167-
if t && @cached_visible[t] && referenced?(t)
168-
t
169-
else
170-
nil
171-
end
172-
end
173-
end
163+
@cached_type_result[type_name]
174164
end
175165

176166
def field(owner, field_name)
177-
f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
178-
field
179-
elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
180-
entry_point_field
181-
elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
182-
dynamic_field
183-
else
184-
nil
185-
end
186-
if f.is_a?(Array)
187-
visible_f = nil
188-
f.each do |f_defn|
189-
if @cached_visible_fields[owner][f_defn]
190-
191-
if visible_f.nil?
192-
visible_f = f_defn
193-
else
194-
raise_duplicate_definition(visible_f, f_defn)
195-
end
196-
end
197-
end
198-
visible_f&.ensure_loaded
199-
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
200-
f
201-
else
202-
nil
203-
end
167+
@cached_field_result[owner][field_name]
204168
end
205169

206170
def fields(owner)
@@ -306,6 +270,7 @@ def visible_enum_value?(enum_value, _ctx = nil)
306270
def preload
307271
load_all_types
308272
@all_types.each do |type_name, type_defn|
273+
type(type_name)
309274
if type_defn.kind.fields?
310275
fields(type_defn).each do |f|
311276
field(type_defn, f.graphql_name)
@@ -341,6 +306,60 @@ def preload
341306

342307
private
343308

309+
def compute_type(type_name)
310+
t = @visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
311+
if t
312+
if t.is_a?(Array)
313+
vis_t = nil
314+
t.each do |t_defn|
315+
if @cached_visible[t_defn] && referenced?(t_defn)
316+
if vis_t.nil?
317+
vis_t = t_defn
318+
else
319+
raise_duplicate_definition(vis_t, t_defn)
320+
end
321+
end
322+
end
323+
vis_t
324+
else
325+
if t && @cached_visible[t] && referenced?(t)
326+
t
327+
else
328+
nil
329+
end
330+
end
331+
end
332+
end
333+
334+
def compute_field(owner, field_name)
335+
f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
336+
field
337+
elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
338+
entry_point_field
339+
elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
340+
dynamic_field
341+
else
342+
nil
343+
end
344+
if f.is_a?(Array)
345+
visible_f = nil
346+
f.each do |f_defn|
347+
if @cached_visible_fields[owner][f_defn]
348+
if visible_f.nil?
349+
visible_f = f_defn
350+
else
351+
raise_duplicate_definition(visible_f, f_defn)
352+
end
353+
end
354+
end
355+
visible_f&.ensure_loaded
356+
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
357+
f
358+
else
359+
nil
360+
end
361+
end
362+
344363
def non_duplicate_items(definitions, visibility_cache)
345364
non_dups = []
346365
names = Set.new

0 commit comments

Comments
 (0)