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]