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

- old
+ new

@@ -1,52 +1,50 @@ # frozen_string_literal: true module GraphQL module Metrics class Tracer - # NOTE: These constants come from the graphql ruby gem. - GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY = 'execute_multiplex' - GRAPHQL_GEM_PARSING_KEY = 'parse' + # NOTE: These constants come from the graphql ruby gem and are in "chronological" order based on the phases + # of execution of the graphql-ruby gem. Most of them can be run multiple times when multiplexing multiple queries. + GRAPHQL_GEM_LEXING_KEY = 'lex' # may not trigger if the query is passed in pre-parsed + GRAPHQL_GEM_PARSING_KEY = 'parse' # may not trigger if the query is passed in pre-parsed + GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY = 'execute_multiplex' # wraps everything below this line; only run once GRAPHQL_GEM_VALIDATION_KEY = 'validate' - GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY = 'analyze_multiplex' + GRAPHQL_GEM_ANALYZE_MULTIPLEX_KEY = 'analyze_multiplex' # wraps all `analyze_query`s; only run once 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' ] def trace(key, data, &block) # NOTE: Context doesn't exist yet during lexing, parsing. - possible_context = data[:query]&.context - - skip_tracing = possible_context&.fetch(GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS, false) + context = data[:query]&.context + skip_tracing = context&.fetch(GraphQL::Metrics::SKIP_GRAPHQL_METRICS_ANALYSIS, false) 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_EXECUTE_MULTIPLEX_KEY - return capture_multiplex_start_time(&block) + when GRAPHQL_GEM_LEXING_KEY + return capture_lexing_time(&block) when GRAPHQL_GEM_PARSING_KEY return capture_parsing_time(&block) + when GRAPHQL_GEM_EXECUTE_MULTIPLEX_KEY + return capture_multiplex_start_time(&block) when GRAPHQL_GEM_VALIDATION_KEY - context = possible_context return capture_validation_time(context, &block) 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 if context[SKIP_FIELD_AND_ARGUMENT_METRICS] return yield unless GraphQL::Metrics.timings_capture_enabled?(data[:query].context) context_key = case key when GRAPHQL_GEM_TRACING_FIELD_KEY GraphQL::Metrics::INLINE_FIELD_TIMINGS @@ -64,11 +62,13 @@ PreContext = Struct.new( :multiplex_start_time, :multiplex_start_time_monotonic, :parsing_start_time_offset, - :parsing_duration + :parsing_duration, + :lexing_start_time_offset, + :lexing_duration ) do def reset self[:multiplex_start_time] = nil self[:multiplex_start_time_monotonic] = nil self[:parsing_start_time_offset] = nil @@ -88,10 +88,22 @@ pre_context.multiplex_start_time_monotonic = GraphQL::Metrics.current_time_monotonic yield end + def capture_lexing_time + # GraphQL::Query#result fires `lex` before the `execute_multiplex` event, so sometimes + # `pre_context.multiplex_start_time_monotonic` isn't set. + lexing_offset_time = pre_context.multiplex_start_time_monotonic || GraphQL::Metrics.current_time_monotonic + timed_result = GraphQL::Metrics.time(lexing_offset_time) { yield } + + pre_context.lexing_start_time_offset = timed_result.start_time + pre_context.lexing_duration = timed_result.duration + + timed_result.result + end + def capture_parsing_time # 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 } @@ -102,24 +114,28 @@ timed_result.result end # Also consolidates parsing timings (if any) from the `pre_context` def capture_validation_time(context) + # Queries may already be lexed and parsed before execution (whether a single query or multiplex). + # If we don't have those values, use some sane defaults. + if pre_context.lexing_duration.nil? + pre_context.lexing_start_time_offset = pre_context.multiplex_start_time + pre_context.lexing_duration = 0.0 + end if pre_context.parsing_duration.nil? - # 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.multiplex_start_time_monotonic) { yield } ns = context.namespace(CONTEXT_NAMESPACE) ns[MULTIPLEX_START_TIME] = pre_context.multiplex_start_time ns[MULTIPLEX_START_TIME_MONOTONIC] = pre_context.multiplex_start_time_monotonic + ns[LEXING_START_TIME_OFFSET] = pre_context.lexing_start_time_offset + ns[LEXING_DURATION] = pre_context.lexing_duration 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