From 5e22dd5f3ec709cf36d83d7100adf89d01ea46da Mon Sep 17 00:00:00 2001 From: loadkpi Date: Mon, 30 Jun 2025 17:29:35 +0300 Subject: [PATCH 01/11] update Appraisals --- Appraisals | 8 +++ gemfiles/graphql_2.3.gemfile | 14 ++++++ gemfiles/graphql_2.3.gemfile.lock | 80 ++++++++++++++++++++++++++++++ gemfiles/graphql_2.4.gemfile | 14 ++++++ gemfiles/graphql_2.4.gemfile.lock | 82 +++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+) create mode 100644 gemfiles/graphql_2.3.gemfile create mode 100644 gemfiles/graphql_2.3.gemfile.lock create mode 100644 gemfiles/graphql_2.4.gemfile create mode 100644 gemfiles/graphql_2.4.gemfile.lock diff --git a/Appraisals b/Appraisals index a52fb26..9700591 100644 --- a/Appraisals +++ b/Appraisals @@ -25,3 +25,11 @@ end appraise "graphql-2.2" do gem "graphql", "~> 2.2.0" end + +appraise "graphql-2.3" do + gem "graphql", "~> 2.3.0" +end + +appraise "graphql-2.4" do + gem "graphql", "~> 2.4.0" +end diff --git a/gemfiles/graphql_2.3.gemfile b/gemfiles/graphql_2.3.gemfile new file mode 100644 index 0000000..c1ecbaf --- /dev/null +++ b/gemfiles/graphql_2.3.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "graphql", "~> 2.3.0" + +group :development, :test do + gem "pry" + gem "pry-inline" + gem "pry-byebug", platform: :mri + gem "graphql-batch" +end + +gemspec path: "../" diff --git a/gemfiles/graphql_2.3.gemfile.lock b/gemfiles/graphql_2.3.gemfile.lock new file mode 100644 index 0000000..247dfc3 --- /dev/null +++ b/gemfiles/graphql_2.3.gemfile.lock @@ -0,0 +1,80 @@ +PATH + remote: .. + specs: + yabeda-graphql (0.2.3) + graphql (>= 1.9, < 3) + yabeda (~> 0.2) + +GEM + remote: https://rubygems.org/ + specs: + anyway_config (2.7.2) + ruby-next-core (~> 1.0) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + base64 (0.3.0) + byebug (12.0.0) + coderay (1.1.3) + concurrent-ruby (1.3.5) + diff-lcs (1.6.2) + dry-initializer (3.2.0) + fiber-storage (1.0.1) + graphql (2.3.22) + base64 + fiber-storage + graphql-batch (0.6.0) + graphql (>= 1.12.18, < 3) + promise.rb (~> 0.7.2) + method_source (1.1.0) + promise.rb (0.7.4) + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.11.0) + byebug (~> 12.0) + pry (>= 0.13, < 0.16) + pry-inline (1.0.7) + pry (> 0.10.0) + unicode (~> 0.4.4) + rake (13.3.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.4) + ruby-next-core (1.1.2) + thor (1.3.2) + unicode (0.4.4.5) + yabeda (0.13.1) + anyway_config (>= 1.0, < 3) + concurrent-ruby + dry-initializer + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + appraisal + bundler + graphql (~> 2.3.0) + graphql-batch + pry + pry-byebug + pry-inline + rake (~> 13.0) + rspec (~> 3.0) + yabeda-graphql! + +BUNDLED WITH + 2.5.6 diff --git a/gemfiles/graphql_2.4.gemfile b/gemfiles/graphql_2.4.gemfile new file mode 100644 index 0000000..fafa33b --- /dev/null +++ b/gemfiles/graphql_2.4.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "graphql", "~> 2.4.0" + +group :development, :test do + gem "pry" + gem "pry-inline" + gem "pry-byebug", platform: :mri + gem "graphql-batch" +end + +gemspec path: "../" diff --git a/gemfiles/graphql_2.4.gemfile.lock b/gemfiles/graphql_2.4.gemfile.lock new file mode 100644 index 0000000..c86c31e --- /dev/null +++ b/gemfiles/graphql_2.4.gemfile.lock @@ -0,0 +1,82 @@ +PATH + remote: .. + specs: + yabeda-graphql (0.2.3) + graphql (>= 1.9, < 3) + yabeda (~> 0.2) + +GEM + remote: https://rubygems.org/ + specs: + anyway_config (2.7.2) + ruby-next-core (~> 1.0) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + base64 (0.3.0) + byebug (12.0.0) + coderay (1.1.3) + concurrent-ruby (1.3.5) + diff-lcs (1.6.2) + dry-initializer (3.2.0) + fiber-storage (1.0.1) + graphql (2.4.17) + base64 + fiber-storage + logger + graphql-batch (0.6.0) + graphql (>= 1.12.18, < 3) + promise.rb (~> 0.7.2) + logger (1.7.0) + method_source (1.1.0) + promise.rb (0.7.4) + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.11.0) + byebug (~> 12.0) + pry (>= 0.13, < 0.16) + pry-inline (1.0.7) + pry (> 0.10.0) + unicode (~> 0.4.4) + rake (13.3.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.4) + ruby-next-core (1.1.2) + thor (1.3.2) + unicode (0.4.4.5) + yabeda (0.13.1) + anyway_config (>= 1.0, < 3) + concurrent-ruby + dry-initializer + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + appraisal + bundler + graphql (~> 2.4.0) + graphql-batch + pry + pry-byebug + pry-inline + rake (~> 13.0) + rspec (~> 3.0) + yabeda-graphql! + +BUNDLED WITH + 2.5.6 From 21e6b8a26a96240426d1cc299e449dfc24122cdb Mon Sep 17 00:00:00 2001 From: loadkpi Date: Mon, 30 Jun 2025 17:32:19 +0300 Subject: [PATCH 02/11] update CI matrix --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 72700a3..428eb4b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,10 @@ jobs: fail-fast: false matrix: include: + - ruby: "3.4" + graphql-ruby: "2.4" + - ruby: "3.3" + graphql-ruby: "2.3" - ruby: "3.3" graphql-ruby: "2.2" - ruby: "3.2" From a808c1acfe97a71c38c4b98a3f9aa048f503e900 Mon Sep 17 00:00:00 2001 From: loadkpi Date: Mon, 30 Jun 2025 17:33:24 +0300 Subject: [PATCH 03/11] require ostruct --- spec/support/graphql_schema.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/support/graphql_schema.rb b/spec/support/graphql_schema.rb index fbac1d4..96bf930 100644 --- a/spec/support/graphql_schema.rb +++ b/spec/support/graphql_schema.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "graphql" require "graphql/batch" +require "ostruct" class PriceLoader < GraphQL::Batch::Loader def perform(ids) From a1f6dfacf5e336a4fb023d85285394b58b9168ed Mon Sep 17 00:00:00 2001 From: loadkpi Date: Mon, 30 Jun 2025 18:31:27 +0300 Subject: [PATCH 04/11] draft fix --- lib/yabeda/graphql.rb | 8 +- .../graphql/depreacated_yabeda_tracing.rb | 100 ++++++++++++++++++ lib/yabeda/graphql/yabeda_tracing.rb | 60 +++++------ 3 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 lib/yabeda/graphql/depreacated_yabeda_tracing.rb diff --git a/lib/yabeda/graphql.rb b/lib/yabeda/graphql.rb index 82b1fa4..c270d21 100644 --- a/lib/yabeda/graphql.rb +++ b/lib/yabeda/graphql.rb @@ -1,6 +1,7 @@ require "yabeda" require "yabeda/graphql/version" require "yabeda/graphql/yabeda_tracing" +require "yabeda/graphql/depreacated_yabeda_tracing" require "yabeda/graphql/instrumentation" module Yabeda @@ -31,7 +32,12 @@ class Error < StandardError; end def self.use(schema) schema.instrument(:query, Instrumentation.new) - schema.use YabedaTracing, trace_scalars: true + + if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.3.0") + schema.trace_with YabedaTracing + else + schema.use DeprecatedYabedaTracing, trace_scalars: true + end end end end diff --git a/lib/yabeda/graphql/depreacated_yabeda_tracing.rb b/lib/yabeda/graphql/depreacated_yabeda_tracing.rb new file mode 100644 index 0000000..f2d89cc --- /dev/null +++ b/lib/yabeda/graphql/depreacated_yabeda_tracing.rb @@ -0,0 +1,100 @@ +require "graphql/tracing/platform_tracing" + +module Yabeda + module GraphQL + class DeprecatedYabedaTracing < ::GraphQL::Tracing::PlatformTracing + + self.platform_keys = { + 'lex' => "graphql.lex", + 'parse' => "graphql.parse", + 'validate' => "graphql.validate", + 'analyze_query' => "graphql.analyze", + 'analyze_multiplex' => "graphql.analyze", + 'execute_multiplex' => "graphql.execute", + 'execute_query' => "graphql.execute", + 'execute_query_lazy' => "graphql.execute", + 'execute_field' => "graphql.execute", + 'execute_field_lazy' => "graphql.execute" + } + + def platform_trace(platform_key, key, data, &block) + start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC + result = block.call + duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start + + case key + when "execute_field", "execute_field_lazy" + field, path, query = extract_field_trace_data(data) + + tags = extract_field_tags(field) + if path.length == 1 + return result if key == "execute_field" && query.schema.lazy?(result) + + if query.query? + instrument_query_execution(tags) + elsif query.mutation? + instrument_mutation_execution(tags) + elsif query.subscription? + # Not implemented yet + end + else + instrument_field_execution(query, path, tags, duration) + end + end + + result + end + + # See https://graphql-ruby.org/api-doc/1.10.5/GraphQL/Tracing + def extract_field_trace_data(data) + if data[:context] # Legacy non-interpreter mode + [data[:context].field, data[:context].path, data[:context].query] + else # Interpreter mode + data.values_at(:field, :path, :query) + end + end + + def extract_field_tags(field) + owner = field.respond_to?(:owner) ? field.owner : field.metadata[:type_class].owner + { + type: owner.graphql_name, + field: field.graphql_name, + deprecated: !field.deprecation_reason.nil?, + } + end + + def instrument_field_execution(query, path, tags, duration) + cache(query)[path][:tags] = tags + cache(query)[path][:duration] += duration + end + + def instrument_mutation_execution(tags) + tags = { name: tags[:field], deprecated: tags[:deprecated] } + Yabeda.graphql.mutation_fields_count.increment(tags) + end + + def instrument_query_execution(tags) + tags = { name: tags[:field], deprecated: tags[:deprecated] } + Yabeda.graphql.query_fields_count.increment(tags) + end + + def cache(query) + query.context.namespace(Yabeda::GraphQL)[:field_call_cache] + end + + def platform_field_key(type, field) + "#{type.graphql_name}.#{field.graphql_name}" + end + + # We don't use these yet, but graphql-ruby require us to declare them + + def platform_authorized_key(type) + "#{type.graphql_name}.authorized" + end + + def platform_resolve_type_key(type) + "#{type.graphql_name}.resolve_type" + end + end + end +end diff --git a/lib/yabeda/graphql/yabeda_tracing.rb b/lib/yabeda/graphql/yabeda_tracing.rb index 9aef0b3..5ba40e1 100644 --- a/lib/yabeda/graphql/yabeda_tracing.rb +++ b/lib/yabeda/graphql/yabeda_tracing.rb @@ -2,50 +2,42 @@ module Yabeda module GraphQL - class YabedaTracing < ::GraphQL::Tracing::PlatformTracing - - self.platform_keys = { - 'lex' => "graphql.lex", - 'parse' => "graphql.parse", - 'validate' => "graphql.validate", - 'analyze_query' => "graphql.analyze", - 'analyze_multiplex' => "graphql.analyze", - 'execute_multiplex' => "graphql.execute", - 'execute_query' => "graphql.execute", - 'execute_query_lazy' => "graphql.execute", - 'execute_field' => "graphql.execute", - 'execute_field_lazy' => "graphql.execute" - } - - def platform_trace(platform_key, key, data, &block) + module YabedaTracing + def execute_field(field:, query:, ast_node:, arguments:, object:, &block) start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC result = block.call duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start - case key - when "execute_field", "execute_field_lazy" - field, path, query = extract_field_trace_data(data) - - tags = extract_field_tags(field) - if path.length == 1 - return result if key == "execute_field" && query.schema.lazy?(result) - - if query.query? - instrument_query_execution(tags) - elsif query.mutation? - instrument_mutation_execution(tags) - elsif query.subscription? - # Not implemented yet - end - else - instrument_field_execution(query, path, tags, duration) + tags = extract_field_tags(field) + + # Create a unique path for each field execution to ensure proper counting + path = [field.owner.graphql_name, field.graphql_name, object.object_id.to_s] + + # Check if this is a root field by looking at the parent type + parent_type = field.owner + is_root_field = [query.schema.query, query.schema.mutation, query.schema.subscription].include?(parent_type) + + if is_root_field + return result if query.schema.lazy?(result) + + if query.query? + instrument_query_execution(tags) + elsif query.mutation? + instrument_mutation_execution(tags) + elsif query.subscription? + # Not implemented yet end + else + instrument_field_execution(query, path, tags, duration) end result end - # See https://graphql-ruby.org/api-doc/1.10.5/GraphQL/Tracing + def execute_field_lazy(field:, query:, ast_node:, arguments:, object:, &block) + execute_field(field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, &block) + end + def extract_field_trace_data(data) if data[:context] # Legacy non-interpreter mode [data[:context].field, data[:context].path, data[:context].query] From e57893e9c41bf24d9fd680d711d33a06bf408783 Mon Sep 17 00:00:00 2001 From: loadkpi Date: Wed, 2 Jul 2025 23:42:27 +0300 Subject: [PATCH 05/11] fixed for 2.4 --- lib/yabeda/graphql.rb | 14 ++- .../graphql/depreacated_yabeda_tracing.rb | 100 ----------------- lib/yabeda/graphql/instrumentation.rb | 20 ++-- lib/yabeda/graphql/legacy/instrumentation.rb | 31 ++++++ lib/yabeda/graphql/legacy/yabeda_tracing.rb | 104 ++++++++++++++++++ lib/yabeda/graphql/yabeda_tracing.rb | 2 +- 6 files changed, 155 insertions(+), 116 deletions(-) delete mode 100644 lib/yabeda/graphql/depreacated_yabeda_tracing.rb create mode 100644 lib/yabeda/graphql/legacy/instrumentation.rb create mode 100644 lib/yabeda/graphql/legacy/yabeda_tracing.rb diff --git a/lib/yabeda/graphql.rb b/lib/yabeda/graphql.rb index c270d21..edb6a6a 100644 --- a/lib/yabeda/graphql.rb +++ b/lib/yabeda/graphql.rb @@ -1,8 +1,10 @@ +require "graphql/version" require "yabeda" require "yabeda/graphql/version" require "yabeda/graphql/yabeda_tracing" -require "yabeda/graphql/depreacated_yabeda_tracing" require "yabeda/graphql/instrumentation" +require "yabeda/graphql/legacy/yabeda_tracing" +require "yabeda/graphql/legacy/instrumentation" module Yabeda module GraphQL @@ -31,12 +33,12 @@ class Error < StandardError; end end def self.use(schema) - schema.instrument(:query, Instrumentation.new) - - if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.3.0") - schema.trace_with YabedaTracing + if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.2.0") + schema.trace_with Yabeda::GraphQL::Instrumentation + schema.trace_with Yabeda::GraphQL::YabedaTracing else - schema.use DeprecatedYabedaTracing, trace_scalars: true + schema.instrument :query, Legacy::Instrumentation.new + schema.use Legacy::YabedaTracing, trace_scalars: true end end end diff --git a/lib/yabeda/graphql/depreacated_yabeda_tracing.rb b/lib/yabeda/graphql/depreacated_yabeda_tracing.rb deleted file mode 100644 index f2d89cc..0000000 --- a/lib/yabeda/graphql/depreacated_yabeda_tracing.rb +++ /dev/null @@ -1,100 +0,0 @@ -require "graphql/tracing/platform_tracing" - -module Yabeda - module GraphQL - class DeprecatedYabedaTracing < ::GraphQL::Tracing::PlatformTracing - - self.platform_keys = { - 'lex' => "graphql.lex", - 'parse' => "graphql.parse", - 'validate' => "graphql.validate", - 'analyze_query' => "graphql.analyze", - 'analyze_multiplex' => "graphql.analyze", - 'execute_multiplex' => "graphql.execute", - 'execute_query' => "graphql.execute", - 'execute_query_lazy' => "graphql.execute", - 'execute_field' => "graphql.execute", - 'execute_field_lazy' => "graphql.execute" - } - - def platform_trace(platform_key, key, data, &block) - start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC - result = block.call - duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start - - case key - when "execute_field", "execute_field_lazy" - field, path, query = extract_field_trace_data(data) - - tags = extract_field_tags(field) - if path.length == 1 - return result if key == "execute_field" && query.schema.lazy?(result) - - if query.query? - instrument_query_execution(tags) - elsif query.mutation? - instrument_mutation_execution(tags) - elsif query.subscription? - # Not implemented yet - end - else - instrument_field_execution(query, path, tags, duration) - end - end - - result - end - - # See https://graphql-ruby.org/api-doc/1.10.5/GraphQL/Tracing - def extract_field_trace_data(data) - if data[:context] # Legacy non-interpreter mode - [data[:context].field, data[:context].path, data[:context].query] - else # Interpreter mode - data.values_at(:field, :path, :query) - end - end - - def extract_field_tags(field) - owner = field.respond_to?(:owner) ? field.owner : field.metadata[:type_class].owner - { - type: owner.graphql_name, - field: field.graphql_name, - deprecated: !field.deprecation_reason.nil?, - } - end - - def instrument_field_execution(query, path, tags, duration) - cache(query)[path][:tags] = tags - cache(query)[path][:duration] += duration - end - - def instrument_mutation_execution(tags) - tags = { name: tags[:field], deprecated: tags[:deprecated] } - Yabeda.graphql.mutation_fields_count.increment(tags) - end - - def instrument_query_execution(tags) - tags = { name: tags[:field], deprecated: tags[:deprecated] } - Yabeda.graphql.query_fields_count.increment(tags) - end - - def cache(query) - query.context.namespace(Yabeda::GraphQL)[:field_call_cache] - end - - def platform_field_key(type, field) - "#{type.graphql_name}.#{field.graphql_name}" - end - - # We don't use these yet, but graphql-ruby require us to declare them - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end - end - end -end diff --git a/lib/yabeda/graphql/instrumentation.rb b/lib/yabeda/graphql/instrumentation.rb index af0ab7d..f032fa1 100644 --- a/lib/yabeda/graphql/instrumentation.rb +++ b/lib/yabeda/graphql/instrumentation.rb @@ -1,15 +1,17 @@ module Yabeda module GraphQL - class Instrumentation - def before_query(query) - reset_cache!(query) - end - - def after_query(query) - cache(query).each do |_path, options| - Yabeda.graphql.field_resolve_runtime.measure(options[:tags], options[:duration]) - Yabeda.graphql.fields_request_count.increment(options[:tags]) + module Instrumentation + def execute_multiplex(multiplex:) + queries = multiplex.queries + queries.each { |query| reset_cache!(query) } + result = super + queries.each do |query| + cache(query).each do |_path, options| + Yabeda.graphql.field_resolve_runtime.measure(options[:tags], options[:duration]) + Yabeda.graphql.fields_request_count.increment(options[:tags]) + end end + result end private diff --git a/lib/yabeda/graphql/legacy/instrumentation.rb b/lib/yabeda/graphql/legacy/instrumentation.rb new file mode 100644 index 0000000..18322b8 --- /dev/null +++ b/lib/yabeda/graphql/legacy/instrumentation.rb @@ -0,0 +1,31 @@ +return if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.2.0") + +module Yabeda + module GraphQL + module Legacy + class Instrumentation + def before_query(query) + reset_cache!(query) + end + + def after_query(query) + cache(query).each do |_path, options| + Yabeda.graphql.field_resolve_runtime.measure(options[:tags], options[:duration]) + Yabeda.graphql.fields_request_count.increment(options[:tags]) + end + end + + private + + def cache(query) + query.context.namespace(Yabeda::GraphQL)[:field_call_cache] + end + + def reset_cache!(query) + query.context.namespace(Yabeda::GraphQL)[:field_call_cache] = + Hash.new { |h,k| h[k] = { tags: {}, duration: 0.0 } } + end + end + end + end +end diff --git a/lib/yabeda/graphql/legacy/yabeda_tracing.rb b/lib/yabeda/graphql/legacy/yabeda_tracing.rb new file mode 100644 index 0000000..5e4a527 --- /dev/null +++ b/lib/yabeda/graphql/legacy/yabeda_tracing.rb @@ -0,0 +1,104 @@ +return if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.2.0") + +require "graphql/tracing/platform_tracing" + +module Yabeda + module GraphQL + module Legacy + class YabedaTracing < ::GraphQL::Tracing::PlatformTracing + + self.platform_keys = { + 'lex' => "graphql.lex", + 'parse' => "graphql.parse", + 'validate' => "graphql.validate", + 'analyze_query' => "graphql.analyze", + 'analyze_multiplex' => "graphql.analyze", + 'execute_multiplex' => "graphql.execute", + 'execute_query' => "graphql.execute", + 'execute_query_lazy' => "graphql.execute", + 'execute_field' => "graphql.execute", + 'execute_field_lazy' => "graphql.execute" + } + + def platform_trace(platform_key, key, data, &block) + start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC + result = block.call + duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start + + case key + when "execute_field", "execute_field_lazy" + field, path, query = extract_field_trace_data(data) + + tags = extract_field_tags(field) + if path.length == 1 + return result if key == "execute_field" && query.schema.lazy?(result) + + if query.query? + instrument_query_execution(tags) + elsif query.mutation? + instrument_mutation_execution(tags) + elsif query.subscription? + # Not implemented yet + end + else + instrument_field_execution(query, path, tags, duration) + end + end + + result + end + + # See https://graphql-ruby.org/api-doc/1.10.5/GraphQL/Tracing + def extract_field_trace_data(data) + if data[:context] # Legacy non-interpreter mode + [data[:context].field, data[:context].path, data[:context].query] + else # Interpreter mode + data.values_at(:field, :path, :query) + end + end + + def extract_field_tags(field) + owner = field.respond_to?(:owner) ? field.owner : field.metadata[:type_class].owner + { + type: owner.graphql_name, + field: field.graphql_name, + deprecated: !field.deprecation_reason.nil?, + } + end + + def instrument_field_execution(query, path, tags, duration) + cache(query)[path][:tags] = tags + cache(query)[path][:duration] += duration + end + + def instrument_mutation_execution(tags) + tags = { name: tags[:field], deprecated: tags[:deprecated] } + Yabeda.graphql.mutation_fields_count.increment(tags) + end + + def instrument_query_execution(tags) + tags = { name: tags[:field], deprecated: tags[:deprecated] } + Yabeda.graphql.query_fields_count.increment(tags) + end + + def cache(query) + query.context.namespace(Yabeda::GraphQL)[:field_call_cache] + end + + def platform_field_key(type, field) + "#{type.graphql_name}.#{field.graphql_name}" + end + + # We don't use these yet, but graphql-ruby require us to declare them + + def platform_authorized_key(type) + "#{type.graphql_name}.authorized" + end + + def platform_resolve_type_key(type) + "#{type.graphql_name}.resolve_type" + end + end + end + end +end diff --git a/lib/yabeda/graphql/yabeda_tracing.rb b/lib/yabeda/graphql/yabeda_tracing.rb index 5ba40e1..c19bbec 100644 --- a/lib/yabeda/graphql/yabeda_tracing.rb +++ b/lib/yabeda/graphql/yabeda_tracing.rb @@ -1,4 +1,4 @@ -require "graphql/tracing/platform_tracing" +#require "graphql/tracing/platform_tracing" module Yabeda module GraphQL From 7cfa4b653a63191960d42277e3cbc83363ef665c Mon Sep 17 00:00:00 2001 From: loadkpi Date: Wed, 2 Jul 2025 23:44:34 +0300 Subject: [PATCH 06/11] graphql 2.5 --- .github/workflows/test.yml | 2 ++ Appraisals | 4 ++++ gemfiles/graphql_2.5.gemfile | 14 ++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 gemfiles/graphql_2.5.gemfile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 428eb4b..a136641 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,8 @@ jobs: fail-fast: false matrix: include: + - ruby: "3.4" + graphql-ruby: "2.5" - ruby: "3.4" graphql-ruby: "2.4" - ruby: "3.3" diff --git a/Appraisals b/Appraisals index 9700591..f6bb5fd 100644 --- a/Appraisals +++ b/Appraisals @@ -33,3 +33,7 @@ end appraise "graphql-2.4" do gem "graphql", "~> 2.4.0" end + +appraise "graphql-2.5" do + gem "graphql", "~> 2.4.0" +end diff --git a/gemfiles/graphql_2.5.gemfile b/gemfiles/graphql_2.5.gemfile new file mode 100644 index 0000000..ac3512e --- /dev/null +++ b/gemfiles/graphql_2.5.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "graphql", "~> 2.5.0" + +group :development, :test do + gem "pry" + gem "pry-inline" + gem "pry-byebug", platform: :mri + gem "graphql-batch" +end + +gemspec path: "../" From dc1dffe88c88cf21f14af3a0b2cb37e277cab0d7 Mon Sep 17 00:00:00 2001 From: loadkpi Date: Wed, 2 Jul 2025 23:48:55 +0300 Subject: [PATCH 07/11] graphql 2.5 --- Appraisals | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Appraisals b/Appraisals index f6bb5fd..61d5b77 100644 --- a/Appraisals +++ b/Appraisals @@ -35,5 +35,5 @@ appraise "graphql-2.4" do end appraise "graphql-2.5" do - gem "graphql", "~> 2.4.0" + gem "graphql", "~> 2.5.0" end From a1d1808e0a13e02a632ed4e6ca21c4296b700a39 Mon Sep 17 00:00:00 2001 From: loadkpi Date: Wed, 2 Jul 2025 23:54:27 +0300 Subject: [PATCH 08/11] Gem::Version if --- lib/yabeda/graphql.rb | 8 ++++++-- lib/yabeda/graphql/legacy/instrumentation.rb | 2 -- lib/yabeda/graphql/legacy/yabeda_tracing.rb | 2 -- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/yabeda/graphql.rb b/lib/yabeda/graphql.rb index edb6a6a..c8b4286 100644 --- a/lib/yabeda/graphql.rb +++ b/lib/yabeda/graphql.rb @@ -1,10 +1,14 @@ require "graphql/version" + require "yabeda" require "yabeda/graphql/version" require "yabeda/graphql/yabeda_tracing" require "yabeda/graphql/instrumentation" -require "yabeda/graphql/legacy/yabeda_tracing" -require "yabeda/graphql/legacy/instrumentation" + +if Gem::Version.new(::GraphQL::VERSION) < Gem::Version.new("2.2.0") + require "yabeda/graphql/legacy/yabeda_tracing" + require "yabeda/graphql/legacy/instrumentation" +end module Yabeda module GraphQL diff --git a/lib/yabeda/graphql/legacy/instrumentation.rb b/lib/yabeda/graphql/legacy/instrumentation.rb index 18322b8..452705a 100644 --- a/lib/yabeda/graphql/legacy/instrumentation.rb +++ b/lib/yabeda/graphql/legacy/instrumentation.rb @@ -1,5 +1,3 @@ -return if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.2.0") - module Yabeda module GraphQL module Legacy diff --git a/lib/yabeda/graphql/legacy/yabeda_tracing.rb b/lib/yabeda/graphql/legacy/yabeda_tracing.rb index 5e4a527..3e82bb1 100644 --- a/lib/yabeda/graphql/legacy/yabeda_tracing.rb +++ b/lib/yabeda/graphql/legacy/yabeda_tracing.rb @@ -1,5 +1,3 @@ -return if Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new( "2.2.0") - require "graphql/tracing/platform_tracing" module Yabeda From 4cfcb9e3974c42ff512238f043bd5f3b31f9d74f Mon Sep 17 00:00:00 2001 From: loadkpi Date: Thu, 3 Jul 2025 00:13:32 +0300 Subject: [PATCH 09/11] fix path resolving for a new version --- lib/yabeda/graphql/yabeda_tracing.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/yabeda/graphql/yabeda_tracing.rb b/lib/yabeda/graphql/yabeda_tracing.rb index c19bbec..4375bc3 100644 --- a/lib/yabeda/graphql/yabeda_tracing.rb +++ b/lib/yabeda/graphql/yabeda_tracing.rb @@ -6,18 +6,13 @@ module YabedaTracing def execute_field(field:, query:, ast_node:, arguments:, object:, &block) start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC result = block.call + duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start tags = extract_field_tags(field) + path = query.context.current_path - # Create a unique path for each field execution to ensure proper counting - path = [field.owner.graphql_name, field.graphql_name, object.object_id.to_s] - - # Check if this is a root field by looking at the parent type - parent_type = field.owner - is_root_field = [query.schema.query, query.schema.mutation, query.schema.subscription].include?(parent_type) - - if is_root_field + if path.length == 1 return result if query.schema.lazy?(result) if query.query? From 7cba2e97ff855eb11c69e5bc9c7bd9a6cb651521 Mon Sep 17 00:00:00 2001 From: loadkpi Date: Thu, 3 Jul 2025 00:17:31 +0300 Subject: [PATCH 10/11] rm unused functions --- lib/yabeda/graphql/yabeda_tracing.rb | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/yabeda/graphql/yabeda_tracing.rb b/lib/yabeda/graphql/yabeda_tracing.rb index 4375bc3..dd91eed 100644 --- a/lib/yabeda/graphql/yabeda_tracing.rb +++ b/lib/yabeda/graphql/yabeda_tracing.rb @@ -1,12 +1,9 @@ -#require "graphql/tracing/platform_tracing" - module Yabeda module GraphQL module YabedaTracing def execute_field(field:, query:, ast_node:, arguments:, object:, &block) start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC result = block.call - duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start tags = extract_field_tags(field) @@ -33,14 +30,6 @@ def execute_field_lazy(field:, query:, ast_node:, arguments:, object:, &block) execute_field(field: field, query: query, ast_node: ast_node, arguments: arguments, object: object, &block) end - def extract_field_trace_data(data) - if data[:context] # Legacy non-interpreter mode - [data[:context].field, data[:context].path, data[:context].query] - else # Interpreter mode - data.values_at(:field, :path, :query) - end - end - def extract_field_tags(field) owner = field.respond_to?(:owner) ? field.owner : field.metadata[:type_class].owner { @@ -69,19 +58,6 @@ def cache(query) query.context.namespace(Yabeda::GraphQL)[:field_call_cache] end - def platform_field_key(type, field) - "#{type.graphql_name}.#{field.graphql_name}" - end - - # We don't use these yet, but graphql-ruby require us to declare them - - def platform_authorized_key(type) - "#{type.graphql_name}.authorized" - end - - def platform_resolve_type_key(type) - "#{type.graphql_name}.resolve_type" - end end end end From fbb93c72fe4863f6d55f8f67d0227fd2df0bf7cf Mon Sep 17 00:00:00 2001 From: loadkpi Date: Thu, 3 Jul 2025 00:17:51 +0300 Subject: [PATCH 11/11] graphql_2.5.gemfile.lock --- gemfiles/graphql_2.5.gemfile.lock | 82 +++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 gemfiles/graphql_2.5.gemfile.lock diff --git a/gemfiles/graphql_2.5.gemfile.lock b/gemfiles/graphql_2.5.gemfile.lock new file mode 100644 index 0000000..d525bab --- /dev/null +++ b/gemfiles/graphql_2.5.gemfile.lock @@ -0,0 +1,82 @@ +PATH + remote: .. + specs: + yabeda-graphql (0.2.3) + graphql (>= 1.9, < 3) + yabeda (~> 0.2) + +GEM + remote: https://rubygems.org/ + specs: + anyway_config (2.7.2) + ruby-next-core (~> 1.0) + appraisal (2.5.0) + bundler + rake + thor (>= 0.14.0) + base64 (0.3.0) + byebug (12.0.0) + coderay (1.1.3) + concurrent-ruby (1.3.5) + diff-lcs (1.6.2) + dry-initializer (3.2.0) + fiber-storage (1.0.1) + graphql (2.5.9) + base64 + fiber-storage + logger + graphql-batch (0.6.0) + graphql (>= 1.12.18, < 3) + promise.rb (~> 0.7.2) + logger (1.7.0) + method_source (1.1.0) + promise.rb (0.7.4) + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.11.0) + byebug (~> 12.0) + pry (>= 0.13, < 0.16) + pry-inline (1.0.7) + pry (> 0.10.0) + unicode (~> 0.4.4) + rake (13.3.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.4) + ruby-next-core (1.1.2) + thor (1.3.2) + unicode (0.4.4.5) + yabeda (0.13.1) + anyway_config (>= 1.0, < 3) + concurrent-ruby + dry-initializer + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + appraisal + bundler + graphql (~> 2.5.0) + graphql-batch + pry + pry-byebug + pry-inline + rake (~> 13.0) + rspec (~> 3.0) + yabeda-graphql! + +BUNDLED WITH + 2.5.23