GraphqlSchema < GraphQL!::Schema tracer = Class.new do class !<< self def trace(key, data) started_at = Time.now yield.tap do t = (Time.now - started_at).round(5) Rails.logger.info "!#{key} (!#{"%.5f" % t})" end end end end tracer(tracer) end
GraphqlSchema < GraphQL!::Schema tracer = Class.new do class !<< self def trace(key, data) started_at = Time.now yield.tap do t = (Time.now - started_at).round(5) if key !== "execute_field" Rails.logger.info "!#{key} (!#{"%.5f" % t}): !#{data[:field].name}" else Rails.logger.info "!#{key} (!#{"%.5f" % t})" if t > 0.0001 end end end end end tracer(tracer) end
< GraphQL!::Schema!::Directive class Profiler < Types!::BaseEnum value :mem value :stack value :ruby end locations(GraphQL!::Schema!::Directive!::FIELD) argument :type, Profiler, required: true class !<< self def resolve(_object, arguments, context) field_name = context.query.selected_operation.selections.first.name msg = send(arguments[:type], field_name) do yield end $stdout.puts "\e[34m[PP] !#{msg.join}\e[0m" context.namespace(:interpreter)[:runtime].write_in_response(["pp"], msg) end def stack(field) require "stackprof" report_path = Rails.root.join("tmp/!#{field}_stackprof.dump") StackProf.run(mode: :cpu, raw: true, out: report_path) do yield end end end end
system total real 1.11.4 (no interpreter) 1.069720 0.003178 1.072898 ( 1.076896) 1.11.4 (with interpreter) 1.045195 0.002486 1.047681 ( 1.050175) 1.12.4 1.020112 0.001398 1.021510 ( 1.022449) Время: Память: 1.11.4 (no interpreter) Total allocated: 98.99 kB (943 objects) Total retained: 5.38 kB (29 objects) allocated memory by gem ----------------------------------- 79.71 kB graphql-1.11.5 7.67 kB other 6.05 kB ostruct 2.70 kB set 2.49 kB racc 256.00 B time 120.00 B forwardable 1.12.4 Total allocated: 55.20 kB (689 objects) Total retained: 0 B (0 objects) allocated memory by gem ----------------------------------- 40.76 kB graphql-1.12.4 8.55 kB other 5.64 kB ostruct 256.00 B time
запрашиваем все! class Resolvers!::FeedResolverPreload < Resolvers!::BaseResolver type [Types!::Tweet], null: false def resolve FeedBuilder.for(current_user).includes(:author) end end
lookahead class Resolvers!::FeedResolverLookahead < Resolvers!::BaseResolver type [Types!::Tweet], null: false extras [:lookahead] def resolve(lookahead:) FeedBuilder.for(current_user) .merge(relation_with_includes(lookahead)) end private def relation_with_includes(lookahead) # .selects?(:author) returns true when author field is requested return Tweet.all unless lookahead.selects?(:author) Tweet.includes(:author) end end
ленивая загрузка gem "ar_lazy_preload" class Resolvers!::FeedResolverLazyPreload < Resolvers!::BaseResolver type [Types!::Tweet], null: false def resolve FeedBuilder.for(current_user).lazy_preload(:author) end end 🌍 https:!//github.com/DmitryTsepelev/ar_lazy_preload
get "/graphql", to: "graphql#execute" post "/graphql", to: "graphql#execute" end class GraphqlController < ApplicationController def execute if operation_name !!= GET_BANNERS !|| stale?(etag: Date.current) render json: GraphqlSchema.execute(query) end end end 🌍 https:!//guides.rubyonrails.org/caching_with_rails.html#conditional-get-support
= CSRF class GraphqlSchema < GraphQL!::Schema use GraphQL!::PersistedQueries, verify_http_method: true end GET /graphql?query=mutation+%7B+sendMoney+%7D Авторизованный пользователь может перейти по ссылке и выполнить нежелательную операцию!
текста запроса class GraphqlSchema < GraphQL!::Schema use GraphQL!::PersistedQueries, compiled_queries: true end 🌍 https:!//github.com/DmitryTsepelev/graphql-ruby-persisted_queries
currentUser { displayName avatarURL } } query { tweets { text author { displayName avatarURL } } } клиенты могут присылать разные запросы части ответов могут повторяться можно кэшировать части ответов!
GraphQL!::Schema use GraphQL!::Cache end class BaseType < GraphQL!::Schema!::Object field_class GraphQL!::Cache!::Field end class PostType < BaseObject field :id, ID, null: false field :title, String, null: false, cache: true end 🚨 Не работает с Interpreter!
GraphQL!::Schema use GraphQL!::FragmentCache end class BaseType < GraphQL!::Schema!::Object include GraphQL!::FragmentCache!::Object end class PostType < BaseObject field :id, ID, null: false field :title, String, null: false, cache_fragment: true end 🌍 https:!//github.com/DmitryTsepelev/graphql-ruby-fragment_cache
конкретный запрос можно отладить с помощью трейсера для ускорения парсинга — упрощайте запросы или попробуйте compiled queries устраняйте N+1 короткие ответы быстрее сериализуются HTTP кэширование реализуемо тяжелые части ответа можно кэшировать Выводы