rmosolgo/graphql-ruby

Query validation never finishes on queries with many directives

alexus37 opened this issue · 3 comments

Describe the bug

Hi,
I have an artificial test case which creates a query with many (non- existing) directives in a query.
If I call .valid? on this query the computation never finishes. This was very fast in 2.1

max_byte_size = 1_000_000
max_directive_count = 100

query_string = "query { viewer { login #{ "@a" * (max_byte_size / 2 - 50) } } }" 
document = GraphQL.parse(query_string, tracer: tracer)
query = GraphQL::Query.new(
      schema,
      document: document,
      context: context,
      variables: variables,
      validate: validate,
    )

query.valid? # never finishes 

Versions

graphql version: 2.2.14
graphql-pro version: 1.27.0

Steps to reproduce

See the the code above.

Expected behavior

This should return static validation errors fast.

Actual behavior

The validation should finish fast.

Hi! sorry for the trouble. I would also expect it to return quickly. I wrote a script based on your example code, but for me, it was faster in 2.2 than 2.1:

Validating lots of undefined directives

require "bundler/inline"

gemfile do
  gem "graphql", "~>2.3.0" # or 2.1, 2.2
end

max_byte_size = 1_000_000
max_directive_count = 100

class MySchema < GraphQL::Schema
  class User < GraphQL::Schema::Object
    field :login, String
  end

  class Query < GraphQL::Schema::Object
    field :viewer, User
  end

  query(Query)
end

query_string = "query { viewer { login #{ "@a" * (max_byte_size / 2 - 50) } } }"
document = GraphQL.parse(query_string)
query = GraphQL::Query.new(
      MySchema,
      document: document,
    )
puts "GraphQL-Ruby v#{GraphQL::VERSION} Starting"
pp query.valid?
puts "Done"

Output:

v2.3.2, 1.92 seconds:

~/code/graphql-ruby $ time ruby directive_test.rb
GraphQL-Ruby v2.3.2 Starting
false
Done
ruby directive_test.rb  1.92s user 0.11s system 97% cpu 2.080 total

v2.2.14, 2.06 seconds:

~/code/graphql-ruby $ time ruby directive_test.rb
GraphQL-Ruby v2.2.12 Starting
false
Done
ruby directive_test.rb  2.06s user 0.15s system 94% cpu 2.336 total

2.1.11, 4.61 seconds:

~/code/graphql-ruby $ time ruby directive_test.rb
GraphQL-Ruby v2.1.11 Starting
false
Done
ruby directive_test.rb  4.61s user 0.20s system 98% cpu 4.859 total

Are there other details that might help me replicate this issue?

In the meantime, you could try:

Let me know what you think!

I did some research and discovered that it is connected to the opentelemetry-instrumentation-graphql. Surprisingly, it runs quickly with version 2.1 but significantly slower with version 2.2. Check out the specifics of a script and the experiments conducted.

require "bundler/inline"

gemfile do
    source 'https://rubygems.org'
    gem "base64", "~>0.2.0"
    gem "graphql", "~>2.1.11" # or 2.1, 2.2
    gem 'opentelemetry-instrumentation-graphql', '~> 0.28.0'
    gem 'opentelemetry-sdk', '~> 1.0'
end

max_byte_size = 1_000_000
max_directive_count = 100


class MySchema < GraphQL::Schema
  class User < GraphQL::Schema::Object
    field :login, String
  end

  class Query < GraphQL::Schema::Object
    field :viewer, User
  end

  query(Query)
  trace_with GraphQL::Tracing::CallLegacyTracers
end

OpenTelemetry::SDK.configure do |c|
  c.use_all('OpenTelemetry::Instrumentation::GraphQL' => { schemas: [MySchema] })
end


query_string = "query { viewer { login #{ "@a" * (max_byte_size / 2 - 50) } } }"
document = GraphQL.parse(query_string)
query = GraphQL::Query.new(
      MySchema,
      document: document,
    )
puts "GraphQL-Ruby v#{GraphQL::VERSION} Starting"
t0 = Time.now
pp query.valid?
puts "Took: #{Time.now - t0}"

Output:

2.1

GraphQL-Ruby v2.1.12 Starting
false
Took: 1.371169

2.2

GraphQL-Ruby v2.2.14 Starting
false
Took: 248.905892

Hey, thanks for sharing that replication script. I identified the regression in the implementation of Node#line and Node#col which are used when preparing error JSON. I improved the implementation in #4949 and released the fix in 2.3.3.

Does updating to that new version work for you?