lib/graphql/metrics/tracer.rb in graphql-metrics-4.0.5 vs lib/graphql/metrics/tracer.rb in graphql-metrics-4.0.6

- old
+ new

@@ -2,15 +2,16 @@ module GraphQL module Metrics class Tracer # NOTE: These constants come from the graphql ruby gem. - GRAPHQL_GEM_LEX_KEY = 'lex' GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY = 'execute_multiplex' GRAPHQL_GEM_PARSING_KEY = 'parse' GRAPHQL_GEM_VALIDATION_KEY = 'validate' - GRAPHQL_GEM_ANALYZE_KEY = 'analyze_query' + GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY = 'analyze_multiplex' + GRAPHQL_GEM_ANALYZE_QUERY_KEY = 'analyze_query' + GRAPHQL_GEM_EXECUTE_QUERY_KEY = 'execute_query' GRAPHQL_GEM_TRACING_FIELD_KEYS = [ GRAPHQL_GEM_TRACING_FIELD_KEY = 'execute_field', GRAPHQL_GEM_TRACING_LAZY_FIELD_KEY = 'execute_field_lazy' ] @@ -22,23 +23,28 @@ return yield if skip_tracing # NOTE: Not all tracing events are handled here, but those that are are handled in this case statement in # chronological order. case key - when GRAPHQL_GEM_LEX_KEY - return setup_tracing(&block) when GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY - return setup_tracing(&block) + return capture_multiplex_start_time(&block) when GRAPHQL_GEM_PARSING_KEY return capture_parsing_time(&block) when GRAPHQL_GEM_VALIDATION_KEY context = possible_context return capture_validation_time(context, &block) - when GRAPHQL_GEM_ANALYZE_KEY + when GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY + # Ensures that we reset potentially long-lived PreContext objects between multiplexs. We reset at this point + # since all parsing and validation will be done by this point, and a GraphQL::Query::Context will exist. + pre_context.reset + return yield + when GRAPHQL_GEM_ANALYZE_QUERY_KEY context = possible_context return capture_analysis_time(context, &block) - + when GRAPHQL_GEM_EXECUTE_QUERY_KEY + context = possible_context + capture_query_start_time(context, &block) when *GRAPHQL_GEM_TRACING_FIELD_KEYS return yield if data[:query].context[SKIP_FIELD_AND_ARGUMENT_METRICS] return yield unless GraphQL::Metrics.timings_capture_enabled?(data[:query].context) context_key = case key @@ -55,16 +61,18 @@ end private PreContext = Struct.new( - :query_start_time, - :query_start_time_monotonic, + :multiplex_start_time, + :multiplex_start_time_monotonic, :parsing_start_time_offset, :parsing_duration ) do - def reset_parsing_timings + def reset + self[:multiplex_start_time] = nil + self[:multiplex_start_time_monotonic] = nil self[:parsing_start_time_offset] = nil self[:parsing_duration] = nil end end @@ -73,58 +81,70 @@ # values in. Uses thread-safe Concurrent::ThreadLocalVar to store a set of values per thread. @pre_context ||= Concurrent::ThreadLocalVar.new(PreContext.new) @pre_context.value end - def setup_tracing - return yield if pre_context.query_start_time + def capture_multiplex_start_time + pre_context.multiplex_start_time = GraphQL::Metrics.current_time + pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic - pre_context.query_start_time = GraphQL::Metrics.current_time - pre_context.query_start_time_monotonic = GraphQL::Metrics.current_time_monotonic - yield end def capture_parsing_time - timed_result = GraphQL::Metrics.time { yield } + # GraphQL::Query#result fires `parse` before the `execute_multiplex` event, so sometimes + # `pre_context.multiplex_start_time_monotonic` isn't set. + parsing_offset_time = pre_context.multiplex_start_time_monotonic || GraphQL::Metrics.current_time_monotonic + timed_result = GraphQL::Metrics.time(parsing_offset_time) { yield } pre_context.parsing_start_time_offset = timed_result.start_time pre_context.parsing_duration = timed_result.duration timed_result.result end + # Also consolidates parsing timings (if any) from the `pre_context` def capture_validation_time(context) if pre_context.parsing_duration.nil? - pre_context.parsing_start_time_offset = 0.0 + # Queries may already be parsed before execution (whether a single query or multiplex). + # If we don't have a parsing start time, use the multiplex start time. + pre_context.parsing_start_time_offset = pre_context.multiplex_start_time + + # If we don't have a duration, consider parsing to have been instantaneous. pre_context.parsing_duration = 0.0 end - timed_result = GraphQL::Metrics.time(pre_context.query_start_time_monotonic) { yield } + timed_result = GraphQL::Metrics.time(pre_context.multiplex_start_time_monotonic) { yield } ns = context.namespace(CONTEXT_NAMESPACE) - ns[QUERY_START_TIME] = pre_context.query_start_time - ns[QUERY_START_TIME_MONOTONIC] = pre_context.query_start_time_monotonic + ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time + ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic ns[PARSING_START_TIME_OFFSET] = pre_context.parsing_start_time_offset ns[PARSING_DURATION] = pre_context.parsing_duration ns[VALIDATION_START_TIME_OFFSET] = timed_result.time_since_offset ns[VALIDATION_DURATION] = timed_result.duration - pre_context.reset_parsing_timings - timed_result.result end def capture_analysis_time(context) ns = context.namespace(CONTEXT_NAMESPACE) - timed_result = GraphQL::Metrics.time(ns[QUERY_START_TIME_MONOTONIC]) { yield } + timed_result = GraphQL::Metrics.time(ns[MULTIPLEX_START_TIME_MONOTONIC]) { yield } ns[ANALYSIS_START_TIME_OFFSET] = timed_result.time_since_offset ns[ANALYSIS_DURATION] = timed_result.duration timed_result.result + end + + def capture_query_start_time(context) + ns = context.namespace(CONTEXT_NAMESPACE) + ns[QUERY_START_TIME] = GraphQL::Metrics.current_time + ns[QUERY_START_TIME_MONOTONIC] = GraphQL::Metrics.current_time_monotonic + + yield end def trace_field(context_key, data) ns = data[:query].context.namespace(CONTEXT_NAMESPACE) query_start_time_monotonic = ns[GraphQL::Metrics::QUERY_START_TIME_MONOTONIC]