Shopify / statsd-instrument

A StatsD client for Ruby apps. Provides metaprogramming methods to inject StatsD instrumentation into your code.
http://shopify.github.io/statsd-instrument
MIT License
570 stars 94 forks source link

Rails reload fails when instrumenting class methods in config/initializers #301

Open dmitri-minkin opened 2 years ago

dmitri-minkin commented 2 years ago

Environment

Ruby interpreter version: ruby 2.7.5 statsd-instrument version: 3.1.2 StatsD backend: Datadog Rails: 7.0.2.3 Machine: Mac M1 OS: Mac OS Monterey 12.3.1

Description

Anything that triggers reloading will throw error from statsd-instrument when instrumenting class methods from config/initializer.

How to reproduce

Given this code in Rails:

# app/services/pin/manager.rb

module Pin
  module Manager
        class << self
           def consume_and_grant
           end
        end
  end
end
# config/initializers/statsd.rb

Rails.application.reloader.to_prepare do
  Pin::Manager.singleton_class.extend(StatsD::Instrument)
  Pin::Manager.singleton_class.statsd_count_success(:consume_and_grant, "Pin.Manager.consume_and_grant")
end

Reload fails in console:

  1. Run in terminal: bin/rails console
  2. Reload in rails console: reload!

Observe this error:

Reloading... ArgumentError: Already instrumented consume_and_grant for from /Users/\<user>/.gem/ruby/2.7.5/gems/statsd-instrument-3.1.2/lib/statsd/instrument.rb:263:in `add_to_method'

Possible cause

StatsD::Instrument having a cache that is likely not being reset on reload

Workaround

You can instrument inside the file that has the module being instrumented:

# app/services/pin/manager.rb

module Pin
  module Manager
        class << self
            def consume_and_grant
            end
        end
  end
end

Pin::Manager.singleton_class.extend(StatsD::Instrument)
Pin::Manager.singleton_class.statsd_count_success(:consume_and_grant, "Pin.Manager.consume_and_grant")
swh-tropic commented 1 year ago

Another workaround that also keeps the logic in an initializer is to use the after_initialize config hook:

Rails.application.config.after_initialize do
  # StatsD config here
end