-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Schema::Visibility is much slower than Schema::Warden #5324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
We've observed that the new `GraphQL::Schema::Visibility` plugin causes a large performance degradation. `GraphQL::Schema::Warden` does not have this issue, so until it's fixed we'll stick with it. ``` ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] Warming up -------------------------------------- Using Visibility 10.000 i/100ms Using Warden 248.000 i/100ms Calculating ------------------------------------- Using Visibility 102.340 (± 2.0%) i/s (9.77 ms/i) - 520.000 in 5.082553s Using Warden 2.471k (± 4.9%) i/s (404.71 μs/i) - 12.400k in 5.030776s Comparison: Using Warden: 2470.9 i/s Using Visibility: 102.3 i/s - 24.14x slower ``` See rmosolgo/graphql-ruby#5324 for details on this issue.
Hey, thanks so much for reporting this. Performance is definitely one of the goals for I took your script and slapped some Stackprof and MemoryProfiler on it and quickly found the problem:
It looks like I didn't benchmark schemas with very large sets of fields/arguments/enum values, so the current implementation has a bad trade-off there. In #5325 I was able to bring Visibility up to parity with Warden. And for sport, I added a new example application: visibility_profile_app = Application.new(using: {
GraphQL::Schema::Visibility => {preload: true, profiles: { all: {} }}
})
# ...
x.report("Using Visibility with Profile") do
visibility_profile_app.execute_query(query, context: { visibility_profile: :all })
end It's 2.5x faster 😎
Could you try the patch from #5325 on your app to see if it looks good in real life? gem "graphql", github: "rmosolgo/graphql-ruby", ref: "fix-vis-for-large-lists" |
This was brilliant, by the way. If you don't mind sharing the script that transformed it, I'd love to recommend it to people in the same situation. Getting the exact same schema structure is a huge deal in replication -- and names don't matter! |
Thanks for the incredibly quick fix!
Woah. I hadn't really dug into visibility profiles yet but clearly I need to get that setup :). Thanks for the tip. I found something interesting when updating my benchmark script to include both the profile thing you pointed out and also using require "bundler/inline"
gemfile do
source "https://rubygems.org"
if ENV["TRY_BRANCH"]
gem "graphql", git: "https://github.com/rmosolgo/graphql-ruby.git", ref: "e2dfdf5954429d8302ef912c89a55ddac0ae6390"
else
gem "graphql", "2.5.2"
end
gem "benchmark-ips"
end
require "benchmark/ips"
require "graphql"
require "open-uri"
SCHEMA_STRING = URI("https://gist.githubusercontent.com/myronmarston/ee6fa2d92d2941b7071459708f7d46d1/raw/8b99a4d9723dc99a7a921cf787c8e6b31a8fc6b7/anonymized_schema.graphql").read
class Application
def initialize(using: {})
@schema = ::GraphQL::Schema.from_definition(
SCHEMA_STRING,
default_resolve: self,
using: using
)
end
def call(parent_type, field, object, args, context)
nil
end
def execute_query(query_string, context: {})
response = ::GraphQL::Query.new(@schema, query_string, variables: {}, context: context).result
unless response.fetch("errors", []).empty?
raise "Query failed: #{response.to_h.inspect}"
end
response
end
end
warden_app = Application.new(using: {
GraphQL::Schema::Warden => {}
})
visibility_app = Application.new(using: {
GraphQL::Schema::Visibility => {preload: true}
})
visibility_profile_app = Application.new(using: {
GraphQL::Schema::Visibility => {preload: true, profiles: { all: {} }, dynamic: true}
})
always_visible_app = Application.new(using: {
GraphQL::Schema::AlwaysVisible => {}
})
query = <<~EOS
query {
field264(
field265: [ENUM_VALUE1000]
) {
__typename
}
}
EOS
Benchmark.ips do |x|
x.report("Using Visibility with dynamic") do
visibility_profile_app.execute_query(query)
end
x.report("Using Visibility with profile") do
visibility_profile_app.execute_query(query, context: { visibility_profile: :all })
end
x.report("Using Visibility") do
visibility_app.execute_query(query)
end
x.report("Using Warden") do
warden_app.execute_query(query)
end
x.report("Using AlwaysVisible") do
always_visible_app.execute_query(query)
end
x.compare!
end Results w/o your fix (not passing
Results w/ your fix (passing
I tried some local benchmarks (on my laptop) of a real query (not the simple
Here you go: https://gist.github.com/myronmarston/4feeeaa5472096d3c1a09eea8d983046 FWIW, goose wrote this for me :). |
Ah... I haven't updated |
Describe the bug
I've been working on deploying an upgrade to one of our internal systems, and discovered that using
GraphQL::Schema::Visibility
is much slower than theGraphQL::Schema::Warden
plugin that we've been using. According to my benchmark, it's more than 20x slower for a very simple query!Versions
graphql
version: 2.5.2rails
(or other framework): N/Aother applicable versions (
graphql-batch
, etc): N/AGraphQL schema
https://gist.github.com/myronmarston/ee6fa2d92d2941b7071459708f7d46d1
(Apologies for the large anonymized schema, but I didn't think I should share our proprietary internal schema and I wasn't able to come up with a smaller schema that reproduced this issue--so I just replaced all the type names and field names to anonymize it.).
GraphQL query
Steps to reproduce
Create a ruby script with this code:
Run it.
Expected behavior
I expect the new
GraphQL::Schema::Visibility
plugin to be as performant asGraphQL::Schema::Warden
. (At least, I need it to be as performant before I can switch to the new plugin, and I'm expectingWarden
to be removed in a future version.)Actual behavior
The
Visibility
plugin is ~20x slower:The text was updated successfully, but these errors were encountered: