diff --git a/Gemfile b/Gemfile index 77180b4d3d..77742028ec 100644 --- a/Gemfile +++ b/Gemfile @@ -8,11 +8,6 @@ gem 'stackprof', platform: :ruby gem 'pry' gem 'pry-stack_explorer', platform: :ruby -if RUBY_VERSION >= "3.0" - gem "libev_scheduler" - gem "evt" -end - if RUBY_VERSION >= "3.2.0" gem "async", "~>2.0" end diff --git a/gemfiles/mongoid_8.gemfile b/gemfiles/mongoid_8.gemfile index 0639b55bfd..5536381b76 100644 --- a/gemfiles/mongoid_8.gemfile +++ b/gemfiles/mongoid_8.gemfile @@ -8,8 +8,6 @@ gem "ruby-prof", platform: :ruby gem "pry" gem "pry-stack_explorer", platform: :ruby gem "mongoid", "~> 8.0" -gem "libev_scheduler" -gem "evt" gem "async" gem "concurrent-ruby", "1.3.4" diff --git a/gemfiles/mongoid_9.gemfile b/gemfiles/mongoid_9.gemfile index b4d8679531..0bd71a82ee 100644 --- a/gemfiles/mongoid_9.gemfile +++ b/gemfiles/mongoid_9.gemfile @@ -7,8 +7,6 @@ gem "ruby-prof", platform: :ruby gem "pry" gem "pry-stack_explorer", platform: :ruby gem "mongoid", "~> 9.0" -gem "libev_scheduler" -gem "evt" gem "async" gemspec path: "../" diff --git a/gemfiles/rails_7.2_postgresql.gemfile b/gemfiles/rails_7.2_postgresql.gemfile index 25ebbae82b..ce33d7ca3a 100644 --- a/gemfiles/rails_7.2_postgresql.gemfile +++ b/gemfiles/rails_7.2_postgresql.gemfile @@ -9,7 +9,6 @@ gem "pry-stack_explorer", platform: :ruby gem "rails", "~> 7.2.0", require: "rails/all" gem "pg", platform: :ruby gem "sequel" -gem "evt" gem "async" gem "libev_scheduler" gem "google-protobuf" diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index 11c33d0ea5..109bf0220a 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -10,7 +10,6 @@ gem "rails", "~> 8.0.0", require: "rails/all" gem "sqlite3" gem "pg", platform: :ruby gem "sequel" -gem "evt" gem "async" gem "google-protobuf" diff --git a/gemfiles/rails_8.1.gemfile b/gemfiles/rails_8.1.gemfile index a55c305eaa..30f62960f0 100644 --- a/gemfiles/rails_8.1.gemfile +++ b/gemfiles/rails_8.1.gemfile @@ -10,7 +10,6 @@ gem "rails", "~> 8.1.0", require: "rails/all" gem "sqlite3" gem "pg", platform: :ruby gem "sequel" -gem "evt" gem "async" gem "google-protobuf" diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile index 635da2eec6..e181203a87 100644 --- a/gemfiles/rails_master.gemfile +++ b/gemfiles/rails_master.gemfile @@ -10,7 +10,6 @@ gem "rails", github: "rails/rails", require: "rails/all", ref: "main" gem 'sqlite3' gem 'pg' gem "sequel" -gem "evt" if RUBY_ENGINE == "ruby" # This doesn't work on truffle-ruby because there's no `IO::READABLE` gem "libev_scheduler" end diff --git a/lib/graphql/analysis/query_complexity.rb b/lib/graphql/analysis/query_complexity.rb index 4a90824b4c..c953855de4 100644 --- a/lib/graphql/analysis/query_complexity.rb +++ b/lib/graphql/analysis/query_complexity.rb @@ -27,14 +27,14 @@ def result future_complexity end when nil - subject.logger.warn <<~GRAPHQL + subject.logger.warn <<~MESSAGE GraphQL-Ruby's complexity cost system is getting some "breaking fixes" in a future version. See the migration notes at https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#complexity_cost_calculation_mode_for-class_method To opt into the future behavior, configure your schema (#{subject.schema.name ? subject.schema.name : subject.schema.ancestors}) with: complexity_cost_calculation_mode(:future) # or `:legacy`, `:compare` - GRAPHQL + MESSAGE max_possible_complexity(mode: :legacy) else raise ArgumentError, "Expected `:future`, `:legacy`, `:compare`, or `nil` from `#{query.schema}.complexity_cost_calculation_mode_for` but got: #{query.schema.complexity_cost_calculation_mode.inspect}" diff --git a/lib/graphql/dataloader/async_dataloader.rb b/lib/graphql/dataloader/async_dataloader.rb index 9781dda03b..5f8b744170 100644 --- a/lib/graphql/dataloader/async_dataloader.rb +++ b/lib/graphql/dataloader/async_dataloader.rb @@ -22,44 +22,44 @@ def run(trace_query_lazy: nil) source_tasks = [] next_source_tasks = [] first_pass = true + sources_condition = Async::Condition.new - manager = spawn_fiber do - trace&.begin_dataloader(self) - while first_pass || !job_fibers.empty? - first_pass = false - fiber_vars = get_fiber_variables + trace&.begin_dataloader(self) + while first_pass || !job_fibers.empty? + first_pass = false + f = spawn_fiber do run_pending_steps(job_fibers, next_job_fibers, source_tasks, jobs_fiber_limit, trace) + end + run_fiber(f) + if f.alive? + raise "GraphQL-Ruby internal error: AsyncDataloader job fiber didn't terminate" + end - Sync do |root_task| - set_fiber_variables(fiber_vars) - while !source_tasks.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) } - while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition, trace)))) - if task.alive? - root_task.yield # give the source task a chance to run - next_source_tasks << task - end + fiber_vars = get_fiber_variables + Sync do |root_task| + set_fiber_variables(fiber_vars) + while !source_tasks.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) } + while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition, trace)))) + if task.alive? + root_task.yield # give the source task a chance to run + next_source_tasks << task end - sources_condition.signal - source_tasks.concat(next_source_tasks) - next_source_tasks.clear end + sources_condition.signal + source_tasks.concat(next_source_tasks) + next_source_tasks.clear end + end - if !@lazies_at_depth.empty? - with_trace_query_lazy(trace_query_lazy) do - run_next_pending_lazies(job_fibers, trace) - run_pending_steps(job_fibers, next_job_fibers, source_tasks, jobs_fiber_limit, trace) - end + if !@lazies_at_depth.empty? + with_trace_query_lazy(trace_query_lazy) do + run_next_pending_lazies(job_fibers, trace) + run_pending_steps(job_fibers, next_job_fibers, source_tasks, jobs_fiber_limit, trace) end end - trace&.end_dataloader(self) - end - - manager.resume - if manager.alive? - raise "Invariant: Manager didn't terminate successfully: #{manager}" end + trace&.end_dataloader(self) rescue UncaughtThrowError => e throw e.tag, e.value diff --git a/spec/graphql/dataloader/nonblocking_dataloader_spec.rb b/spec/graphql/dataloader/nonblocking_dataloader_spec.rb index 9a3bcdf8b0..fa60ccfd19 100644 --- a/spec/graphql/dataloader/nonblocking_dataloader_spec.rb +++ b/spec/graphql/dataloader/nonblocking_dataloader_spec.rb @@ -243,19 +243,5 @@ def self.included(child_class) let(:scheduler_class) { ::DummyScheduler } include NonblockingDataloaderAssertions end - - if RUBY_ENGINE == "ruby" && !ENV["GITHUB_ACTIONS"] - describe "With libev_scheduler" do - require "libev_scheduler" - let(:scheduler_class) { Libev::Scheduler } - include NonblockingDataloaderAssertions - end - - describe "with evt" do - require "evt" - let(:scheduler_class) { Evt::Scheduler } - include NonblockingDataloaderAssertions - end - end end end diff --git a/spec/graphql/dataloader_spec.rb b/spec/graphql/dataloader_spec.rb index a5020de012..743c843e7e 100644 --- a/spec/graphql/dataloader_spec.rb +++ b/spec/graphql/dataloader_spec.rb @@ -1191,17 +1191,34 @@ def assert_last_max_fiber_count(expected_last_max_fiber_count, message = nil) res = schema.execute(query_str, context: { dataloader: fiber_counting_dataloader_class.new }) assert_nil res.context.dataloader.fiber_limit - assert_equal 10, FiberCounting.last_spawn_fiber_count + + extra_shortlived_jobs_fibers = if fiber_counting_dataloader_class < GraphQL::Dataloader::AsyncDataloader + 3 + else + 0 + end + assert_equal 10 + extra_shortlived_jobs_fibers, FiberCounting.last_spawn_fiber_count assert_last_max_fiber_count(9, "No limit works as expected") + extra_shortlived_jobs_fibers = if fiber_counting_dataloader_class < GraphQL::Dataloader::AsyncDataloader + 10 # more here because there are fewer jobs fibers running at any one time + else + 0 + end res = schema.execute(query_str, context: { dataloader: fiber_counting_dataloader_class.new(fiber_limit: 4) }) assert_equal 4, res.context.dataloader.fiber_limit - assert_equal 12, FiberCounting.last_spawn_fiber_count + assert_equal 12 + extra_shortlived_jobs_fibers, FiberCounting.last_spawn_fiber_count assert_last_max_fiber_count(4, "Limit of 4 works as expected") + extra_shortlived_jobs_fibers = if fiber_counting_dataloader_class < GraphQL::Dataloader::AsyncDataloader + 4 + else + 0 + end + res = schema.execute(query_str, context: { dataloader: fiber_counting_dataloader_class.new(fiber_limit: 6) }) assert_equal 6, res.context.dataloader.fiber_limit - assert_equal 8, FiberCounting.last_spawn_fiber_count + assert_equal 8 + extra_shortlived_jobs_fibers, FiberCounting.last_spawn_fiber_count assert_last_max_fiber_count(6, "Limit of 6 works as expected") end @@ -1283,27 +1300,6 @@ def make_schema_from(schema) include DataloaderAssertions end - - if RUBY_ENGINE == "ruby" && !ENV["GITHUB_ACTIONS"] - describe "nonblocking: true with libev" do - require "libev_scheduler" - def make_schema_from(schema) - Class.new(schema) do - use GraphQL::Dataloader, nonblocking: true - end - end - - before do - Fiber.set_scheduler(Libev::Scheduler.new) - end - - after do - Fiber.set_scheduler(nil) - end - - include DataloaderAssertions - end - end end describe "example from #3314" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cd5dd98d88..cf7f3e691e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -43,7 +43,7 @@ end # C methods aren't fair game in non-main Ractors -RUN_RACTOR_TESTS = defined?(::Ractor) && !USING_C_PARSER +RUN_RACTOR_TESTS = defined?(::Ractor) && !USING_C_PARSER && !ENV["SKIP_RACTOR_TESTS"] require "rake" require "graphql/rake_task"