Graphql-ruby: Adding Relay Connections for pagination slows down responses 10x

Created on 19 Feb 2018  路  5Comments  路  Source: rmosolgo/graphql-ruby

Context

I have a functioning GraphQL API using graphql-ruby and graphql-batch. I'm using Sequel for database connections. I wanted to try out the built-in pagination functionality according to the docs here.

However, changing my field to be a connection, (keeping everything else the exact same), my queries take almost ten times longer to complete.

Is there any way to optimize this?

Code Comparisons

Original Pagination Implementation

Code:

field :things, types[Types::Thing] do
  description "List of things"
  argument :limit, types.Int, "Limit the number of results, default 100", default_value: 100

  resolve -> (_obj, args, _ctx) {
    DB::Things.first(args[:limit])
  }
end

Query:

{
  things(limit: 1000) {
    thingField1
    thingField2
    nestedThing {
      nestedThingField1
      nestedThingField2
    }
  }
}

Average response time: 1,300ms

Pagination Implementation with Connections

Code:

connection :things, Types::Thing.connection_type do
  description "List of things"
  resolve -> (_obj, _args, _ctx) {
    DB::Things.all
  }
end

Query:

{ 
 things(first: 1000) {
    edges {
      node {
        thingField1
        thingField2
        nestedThing {
          nestedThingField1
          nestedThingField2
        }
      }
    }
  }
}

Average response time: 10,100ms

Most helpful comment

馃憦 馃檶 Glad to hear it, thanks for such a detailed issue!

All 5 comments

Hi! Yikes!

Can you see any difference in the underlying SQL queries?

For example, I noticed that you added a .all in DB::Things.all. Does that load every record in the table? I bet that would slow it down a lot!

I think you could try removing the .all, and let graphql-ruby's built-in Sequel support perform pagination for you. How does that work for you?

Yeah, it's definitely not limiting in the Sequel query 馃槙
Old code: SELECT * FROM "things" LIMIT 1000
New code: SELECT * FROM "things"

How do I go about getting the connection to work without all? Changing the code to this:

connection :things, Types::Thing.connection_type do
  description "List of things"
  resolve -> (_obj, _args, _ctx) {
    DB::Thing
  }
end

gives me a RuntimeError (No connection implementation to wrap Class (DB::Thing)).

Changing it to not have a custom resolver:

connection :things, Types::Thing.connection_type,
  description: "List of things"

gives me a NoMethodError (undefined method 'things' for nil:NilClass)

Am I missing something?

Hmm, I don't know Sequel too well so it's probably my bad. We need to get a Sequel::Dataset somehow, maybe the .dataset method would do the trick? DB::Thing.dataset? I got the example from: http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/DatasetMethods.html

Ah perfect, that was it!! Average query times are back to normal.

Thanks so much for your help! Closing the issue.

馃憦 馃檶 Glad to hear it, thanks for such a detailed issue!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jtippett picture jtippett  路  3Comments

skanev picture skanev  路  3Comments

peterphan1996 picture peterphan1996  路  3Comments

rmosolgo picture rmosolgo  路  4Comments

dsgoers picture dsgoers  路  3Comments