Skip to content

Commit 0c45b03

Browse files
authored
Merge pull request #5370 from rmosolgo/basic-ractor-support
Minimal Ractor support
2 parents 62d427f + 92b15f3 commit 0c45b03

File tree

22 files changed

+397
-32
lines changed

22 files changed

+397
-32
lines changed

lib/graphql.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def self.scan_with_ruby(graphql_string)
7272
GraphQL::Language::Lexer.tokenize(graphql_string)
7373
end
7474

75-
NOT_CONFIGURED = Object.new
75+
NOT_CONFIGURED = Object.new.freeze
7676
private_constant :NOT_CONFIGURED
7777
module EmptyObjects
7878
EMPTY_HASH = {}.freeze

lib/graphql/dataloader/null_dataloader.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ class Dataloader
99
class NullDataloader < Dataloader
1010
# These are all no-ops because code was
1111
# executed synchronously.
12+
13+
def initialize(*); end
1214
def run; end
1315
def run_isolated; yield; end
16+
def clear_cache; end
1417
def yield(_source)
1518
raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
1619
end
@@ -19,6 +22,10 @@ def append_job
1922
yield
2023
nil
2124
end
25+
26+
def with(*)
27+
raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
28+
end
2229
end
2330
end
2431
end

lib/graphql/execution/interpreter/runtime.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ def current_path
557557
path
558558
end
559559

560-
HALT = Object.new
560+
HALT = Object.new.freeze
561561
def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
562562
case value
563563
when nil

lib/graphql/language/lexer.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ def finished?
2020
@finished
2121
end
2222

23+
def freeze
24+
@scanner = nil
25+
super
26+
end
27+
2328
attr_reader :pos, :tokens_count
2429

2530
def advance
@@ -242,7 +247,7 @@ def raise_parse_error(message, line = line_number, col = column_number)
242247
:SCALAR,
243248
nil,
244249
:FRAGMENT
245-
]
250+
].freeze
246251

247252
# This produces a unique integer for bytes 2 and 3 of each keyword string
248253
# See https://tenderlovemaking.com/2023/09/02/fast-tokenizers-with-stringscanner.html
@@ -271,7 +276,8 @@ module Punctuation
271276
PUNCTUATION_NAME_FOR_BYTE = Punctuation.constants.each_with_object([]) { |name, arr|
272277
punct = Punctuation.const_get(name)
273278
arr[punct.ord] = name
274-
}
279+
}.freeze
280+
275281

276282
QUOTE = '"'
277283
UNICODE_DIGIT = /[0-9A-Za-z]/
@@ -321,6 +327,7 @@ module ByteFor
321327
punct = Punctuation.const_get(punct_name)
322328
FIRST_BYTES[punct.ord] = ByteFor::PUNCTUATION
323329
end
330+
FIRST_BYTES.freeze
324331

325332

326333
# Replace any escaped unicode or whitespace with the _actual_ characters

lib/graphql/language/nodes.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ def position
8383

8484
def to_query_string(printer: GraphQL::Language::Printer.new)
8585
if printer.is_a?(GraphQL::Language::Printer)
86-
@query_string ||= printer.print(self)
86+
if frozen?
87+
@query_string || printer.print(self)
88+
else
89+
@query_string ||= printer.print(self)
90+
end
8791
else
8892
printer.print(self)
8993
end

lib/graphql/language/parser.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require "strscan"
44
require "graphql/language/nodes"
5+
require "graphql/tracing/null_trace"
56

67
module GraphQL
78
module Language

lib/graphql/query/context.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def types
8484

8585
attr_writer :types
8686

87-
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
87+
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path]).freeze
8888
# @!method []=(key, value)
8989
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
9090

lib/graphql/schema.rb

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require "graphql/schema/finder"
88
require "graphql/schema/introspection_system"
99
require "graphql/schema/late_bound_type"
10+
require "graphql/schema/ractor_shareable"
1011
require "graphql/schema/timeout"
1112
require "graphql/schema/type_expression"
1213
require "graphql/schema/unique_within_type"
@@ -148,10 +149,12 @@ def subscriptions=(new_implementation)
148149
end
149150

150151
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
151-
def default_trace_mode(new_mode = nil)
152-
if new_mode
152+
def default_trace_mode(new_mode = NOT_CONFIGURED)
153+
if !NOT_CONFIGURED.equal?(new_mode)
153154
@default_trace_mode = new_mode
154-
elsif defined?(@default_trace_mode)
155+
elsif defined?(@default_trace_mode) &&
156+
!@default_trace_mode.nil? # This `nil?` check seems necessary because of
157+
# Ractors silently initializing @default_trace_mode somehow
155158
@default_trace_mode
156159
elsif superclass.respond_to?(:default_trace_mode)
157160
superclass.default_trace_mode
@@ -365,7 +368,8 @@ def types(context = GraphQL::Query::NullContext.instance)
365368
# @return [Module, nil] A type, or nil if there's no type called `type_name`
366369
def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
367370
if use_visibility_profile
368-
return Visibility::Profile.from_context(context, self).type(type_name)
371+
profile = Visibility::Profile.from_context(context, self)
372+
return profile.type(type_name)
369373
end
370374
local_entry = own_types[type_name]
371375
type_defn = case local_entry
@@ -697,7 +701,21 @@ def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }"
697701
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
698702
end
699703

700-
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
704+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
705+
if use_visibility_profile
706+
profile = Visibility::Profile.from_context(context, self)
707+
parent_type = case type_or_name
708+
when String
709+
profile.type(type_or_name)
710+
when Module
711+
type_or_name
712+
when LateBoundType
713+
profile.type(type_or_name.name)
714+
else
715+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
716+
end
717+
return profile.field(parent_type, field_name)
718+
end
701719
parent_type = case type_or_name
702720
when LateBoundType
703721
get_type(type_or_name.name, context)
@@ -1105,20 +1123,21 @@ def rescue_from(*err_classes, &handler_block)
11051123
end
11061124
end
11071125

1108-
NEW_HANDLER_HASH = ->(h, k) {
1109-
h[k] = {
1110-
class: k,
1111-
handler: nil,
1112-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1113-
}
1114-
}
1115-
11161126
def error_handlers
1117-
@error_handlers ||= {
1118-
class: nil,
1119-
handler: nil,
1120-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1121-
}
1127+
@error_handlers ||= begin
1128+
new_handler_hash = ->(h, k) {
1129+
h[k] = {
1130+
class: k,
1131+
handler: nil,
1132+
subclass_handlers: Hash.new(&new_handler_hash),
1133+
}
1134+
}
1135+
{
1136+
class: nil,
1137+
handler: nil,
1138+
subclass_handlers: Hash.new(&new_handler_hash),
1139+
}
1140+
end
11221141
end
11231142

11241143
# @api private

lib/graphql/schema/argument.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ def statically_coercible?
212212
@statically_coercible = !requires_parent_object
213213
end
214214

215+
def freeze
216+
statically_coercible?
217+
super
218+
end
219+
215220
# Apply the {prepare} configuration to `value`, using methods from `obj`.
216221
# Used by the runtime.
217222
# @api private

lib/graphql/schema/field.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,14 @@ def default_page_size
616616
end
617617
end
618618

619+
def freeze
620+
type
621+
owner_type
622+
arguments_statically_coercible?
623+
connection?
624+
super
625+
end
626+
619627
class MissingReturnTypeError < GraphQL::Error; end
620628
attr_writer :type
621629

0 commit comments

Comments
 (0)