Async Source Execution

AsyncDataloader will run GraphQL::Dataloader::Source#fetch calls in parallel, so that external service calls (like database queries or network calls) don’t have to wait in a queue.

To use AsyncDataloader, hook it up in your schema instead of GraphQL::Dataloader:

- use GraphQL::Dataloader
+ use GraphQL::Dataloader::AsyncDataloader

Also, add the async gem to your project, for example:

bundle add async

Now, GraphQL::Dataloader::AsyncDataloader will create Async::Task instances instead of plain Fibers and the async gem will manage parallelism.

For a demonstration of this behavior, see: https://github.com/rmosolgo/rails-graphql-async-demo

You can also implement manual parallelism using dataloader.yield.

Rails

For Rails, you’ll need Rails 7.1, which properly supports fiber-based concurrency, and you’ll also want to configure Rails to use Fibers for isolation:

class Application < Rails::Application
  # ...
  config.active_support.isolation_level = :fiber
end

ActiveRecord Connections

You can use Dataloader’s Fiber lifecycle hooks to improve ActiveRecord connection handling:

Altogether, it can be improved like this:

def get_fiber_variables
  vars = super
  # Collect the current connection config to pass on:
  vars[:connected_to] = {
    role: ActiveRecord::Base.current_role,
    shard: ActiveRecord::Base.current_shard,
    prevent_writes: ActiveRecord::Base.current_preventing_writes
  }
  vars
end

def set_fiber_variables(vars)
  connection_config = vars.delete(:connected_to)
  # Reset connection config from the parent fiber:
  ActiveRecord::Base.connecting_to(**connection_config)
  super(vars)
end

def cleanup_fiber
  super
  # Release the current connection
  ActiveRecord::Base.connection_pool.release_connection
end

Modify the example according to your database configuration and abstract class hierarchy.

Other Options

You can also manually implement parallelism with Dataloader. See the Dataloader Parallelism guide for details.