Skip to content

Commit 814eab4

Browse files
committed
Support disabling timeout mid-way through
1 parent 4646e43 commit 814eab4

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

lib/graphql/schema/timeout.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,23 @@ def execute_multiplex(multiplex:)
7171
def execute_field(query:, field:, **_rest)
7272
timeout_state = query.context.namespace(@timeout).fetch(:state)
7373
# If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
74-
if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
74+
if timeout_state == false
75+
super
76+
elsif Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
7577
error = GraphQL::Schema::Timeout::TimeoutError.new(field)
7678
# Only invoke the timeout callback for the first timeout
7779
if !timeout_state[:timed_out]
7880
timeout_state[:timed_out] = true
7981
@timeout.handle_timeout(error, query)
82+
timeout_state = query.context.namespace(@timeout).fetch(:state)
8083
end
8184

82-
error
85+
# `handle_timeout` may have set this to be `false`
86+
if timeout_state != false
87+
error
88+
else
89+
super
90+
end
8391
else
8492
super
8593
end
@@ -102,6 +110,15 @@ def handle_timeout(error, query)
102110
# override to do something interesting
103111
end
104112

113+
# Call this method (eg, from {#handle_timeout}) to disable timeout tracking
114+
# for the given query.
115+
# @param query [GraphQL::Query]
116+
# @return [void]
117+
def disable_timeout(query)
118+
query.context.namespace(self)[:state] = false
119+
nil
120+
end
121+
105122
# This error is raised when a query exceeds `max_seconds`.
106123
# Since it's a child of {GraphQL::ExecutionError},
107124
# its message will be added to the response's `errors` key.

spec/graphql/schema/timeout_spec.rb

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ def execute_field(query:, **opts)
99
end
1010
end
1111

12+
class CustomTimeout < GraphQL::Schema::Timeout
13+
def handle_timeout(error, query)
14+
if query.context[:disable_timeout]
15+
disable_timeout(query)
16+
end
17+
super
18+
end
19+
end
20+
1221
let(:max_seconds) { 1 }
13-
let(:timeout_class) { GraphQL::Schema::Timeout }
22+
let(:timeout_class) { CustomTimeout }
1423
let(:timeout_schema) {
1524
nested_sleep_type = Class.new(GraphQL::Schema::Object) do
1625
graphql_name "NestedSleep"
@@ -36,9 +45,13 @@ def nested_sleep(seconds:)
3645

3746
field :sleep_for, Float do
3847
argument :seconds, Float
48+
argument :disable_timeout, "Boolean", default_value: false
3949
end
4050

41-
def sleep_for(seconds:)
51+
def sleep_for(seconds:, disable_timeout:)
52+
if disable_timeout
53+
context[:disable_timeout] = true
54+
end
4255
sleep(seconds)
4356
seconds
4457
end
@@ -254,4 +267,39 @@ def handle_timeout(err, query)
254267
end
255268
end
256269
end
270+
271+
272+
describe "disabling the timeout" do
273+
let(:query_string) {%|
274+
query GetTimeouts($disable: Boolean) {
275+
a: sleepFor(seconds: 0.4)
276+
b: sleepFor(seconds: 0.4)
277+
c: sleepFor(seconds: 0.4, disableTimeout: $disable)
278+
d: sleepFor(seconds: 0.4)
279+
e: sleepFor(seconds: 0.4)
280+
}
281+
|}
282+
283+
it "can be disabled" do
284+
expected_data = {
285+
"a"=>0.4,
286+
"b"=>0.4,
287+
"c"=>0.4,
288+
"d"=>nil,
289+
"e"=>nil,
290+
}
291+
292+
assert_graphql_equal expected_data, result["data"]
293+
294+
disabled_timeout_result = timeout_schema.execute(query_string, context: query_context, variables: { disable: true })
295+
expected_disabled_data = {
296+
"a"=>0.4,
297+
"b"=>0.4,
298+
"c"=>0.4,
299+
"d"=>0.4,
300+
"e"=>0.4,
301+
}
302+
assert_graphql_equal expected_disabled_data, disabled_timeout_result["data"]
303+
end
304+
end
257305
end

0 commit comments

Comments
 (0)