Skip to content

rmosolgo/rails-graphql-async-demo

Repository files navigation

Rails, GraphQL, Async demo

This app demonstrates parallel HTTP and ActiveRecord calls using AsyncDataloader. You could use this to speed up GraphQL-Ruby queries that do long-running external service calls.

To enable the AsyncDataloader, use it in the schema configuration:

- use GraphQL::Dataloader
+ use GraphQL::Dataloader::AsyncDataloader
+ # from a development branch of graphql-ruby

Consider a query like this one:

{
  count1: remoteDataloaderCount(set: "zen")
  count2: remoteDataloaderCount(set: "lrw")
  count3: remoteDataloaderCount(set: "m10")
}

Without AsyncDataloader, these operations run in sequence, for example:

Processing by GraphqlController#execute as */*

Started Sources::RemoteSet / zen
Finished Sources::RemoteSet / zen
Started Sources::RemoteSet / lrw
Finished Sources::RemoteSet / lrw
Started Sources::RemoteSet / m10
Finished Sources::RemoteSet / m10

Completed 200 OK in 219ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 56576)

But, when AsyncDataloader is enabled, the operations run simultaneously:

Processing by GraphqlController#execute as */*

Started Sources::RemoteSet / zen
Started Sources::RemoteSet / lrw
Started Sources::RemoteSet / m10
Finished Sources::RemoteSet / lrw
Finished Sources::RemoteSet / m10
Finished Sources::RemoteSet / zen

Completed 200 OK in 120ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 88201)

HTTP Calls

def fetch(_ids)
raise(ArgumentError, _ids) unless _ids == [:count]
Rails.logger.debug("Started #{self.class} / #{@set}")
res = Net::HTTP.get(URI("https://api.scryfall.com/cards/search?q=set:#{@set}"))
data = JSON.parse(res)
Rails.logger.debug("Finished #{self.class} / #{@set}")
[data["total_cards"]]
end

ActiveRecord Calls

Note: with sqlite3, these won't be parallel because sqlite only supports one operation at a time.

def fetch(_ids)
raise(ArgumentError, _ids) unless _ids == [:count]
Rails.logger.debug("Started #{self.class} / #{@set}")
count = Card.where(set: @set).count(:*)
Rails.logger.debug("Finished #{self.class} / #{@set}")
[count]
end

About

A Rails App with GraphQL-Ruby and Async for parallel HTTP and database queries

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published