rmosolgo/graphql-ruby

Integrate with ActiveRecord::QueryLogs (old marginalia)

mrcasals opened this issue · 7 comments

Is your feature request related to a problem? Please describe.

ActiveRecord::QueryLogs (the old marginalia gem, now bundled in Rails 7) allow us to add comments on SQL queries to detect where is the query triggered from (what controller and action). But when using GraphQL, the comment becomes useless because it's always the same controller and action, so we become blind.

Describe the solution you'd like

ActiveRecord::QueryLogs allow for customization so we could add the current GraphQL query being resolved.

Describe alternatives you've considered

None.

Additional context

None.

Hey, thanks for the suggestion! I think that's a great idea. Here's how you could add that in your own app in the meantime:

# In your Application setup: 
config.active_record.query_log_tags = [ 
  # Rails default keys: 
  :application, :controller, :action, :job,
  # Tell QueryLogs about this new key: 
  { graphql_query: -> { Current.graphql_query_names } }
]

# Assuming `class Current < ActiveSupport::CurrentAttributes` as in https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
# Substitute your own global state object as-needed
module ActiveRecordQueryLogsTrace
  def execute_multiplex(multiplex:)
    Current.graphql_query_names = multiplex.queries.map { |q| q.selected_operation&.name || "anonymous" }
  end 
end 

I think that would tag ActiveRecord queries with the currently running operation(s). Is that what you had in mind?

A deeper integration is also possible, for example, tagging SQL with the currently-running Dataloader::Source or GraphQL field.

This looks good, I'm trying to adapt it to my code because I'm using an old version of the gem, v2.0.14 (sorry, forgot to mention it!). I'm guessing the ActiveRecordQueryLogsTrace module would be used with trace_with(ActiveRecordQueryLogsTrace) in the latest version, so I should use tracer(ActiveRecordQueryLogsTrace) there and adapt it, I'll get back to you!

Thanks!

Here's an approach that would work in 2.0.x:

# Assuming `class Current < ActiveSupport::CurrentAttributes` as in https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
# Substitute your own global state object as-needed
module ActiveRecordQueryLogsInstrumenter
  def self.begin_multiplex(multiplex)
    Current.graphql_query_names = multiplex.queries.map { |q| q.selected_operation&.name || "anonymous" }
  end 

  def self.end_multiplex(_multiplex); end 
end 


# ... 

class MySchema < GraphQL::Schema 
  instrument(:multiplex, ActiveRecordQueryLogsInstrumenter 
end 

(The instrument(...) API was merged into trace_with in recent versions.)

Awesome, it works! Thanks! I just had to change the method names, they should be before_multiplex and after_multiplex, but apart from that everything works fine! Thank you so much ❤️

I'll leave the issue open in case you want to track it to automatically add intergration with QueryLogs, but feel free to close it if needed!

Hello @rmosolgo, it seems that in your example (#4946 (comment)), super needs to be called.

Like this:

# In your Application setup: 
config.active_record.query_log_tags = [ 
  # Rails default keys: 
  :application, :controller, :action, :job,
  # Tell QueryLogs about this new key: 
  { graphql_query: -> { Current.graphql_query_names } }
]

# Assuming `class Current < ActiveSupport::CurrentAttributes` as in https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
# Substitute your own global state object as-needed
module ActiveRecordQueryLogsTrace
  def execute_multiplex(multiplex:)
    Current.graphql_query_names = multiplex.queries.map { |q| q.selected_operation&.name || "anonymous" }
    super
  end 
end 

v2.3.11 will include a new module, GraphQL::Current with some helpers for this: https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/current.rb. Thanks again for the suggestion!

Awesome! this is gonna be very helpful for debugging, thank you ❤ Glad the suggestion was useful!