-
Notifications
You must be signed in to change notification settings - Fork 381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ArgumentError: wrong number of arguments (given 2, expected 0) #1306
Comments
👋 @EvNomad, thanks for reaching out. I'm also not sure where a zero argument initializer for I see your application last has control of the stack frame at One thing I can think of: are you requiring |
@marcotc Thank you for your response! We never had the dd-trace-rb/lib/ddtrace/metrics.rb Line 44 in dba4c21
So, in order to print out the source I had to add the require and it helped (also with the weird airbrake config turned back on) Here is the output of This is our initialiser: require 'ddtrace'
Datadog.configure do |c|
c.tracer.enabled = true
c.runtime_metrics.enabled = true
c.use(:rails, analytics_enabled: true)
c.use(:redis, analytics_enabled: true)
c.use(:sidekiq, analytics_enabled: true)
c.use(:dalli, analytics_enabled: true)
end BTW the only place I found the same stack trace is here: #1163 Any ideas what should be investigated further? Thank you! |
Thank you for pointing out #1163, @EvNomad, I think it helps understand the issue you are seeing. #1163 was caused by concurrent threads racing to call: dd-trace-rb/lib/ddtrace/metrics.rb Lines 44 to 47 in dba4c21
When I reproduced the same error you are having locally, I caught the application half-way through requiring This causes With this in mind: I suspect your environment has multiple threads initializing or We advise against concurrent initialization of the tracer, as If concurrent initialization is a requirement for you, I'd like to understand your setup, as this would be a new requirement for the library. |
Thank you for this idea
It seems like the Airbrake thread produces debug level log which call the tracer and races with the main thread. |
As we are moving towards addressing this issue, with safe concurrent initialization of the tracer, I'll record here steps to reproduce this issue that when I dug into this issue: With Ruby 2.6, revision feb2047 of
This will result in It might take a few tries, given the concurrent nature of the failure. |
If for some reason multiple tracers are initializing concurrently (see customer issue in #1306 or the flaky test in `tracer_integration_spec.rb` in the previous commit), multiple threads can try to run `default_statsd_client` concurrently. Because Ruby `require`s are not atomic (e.g. you can observe classes that are half-loaded), our conditional ```ruby require 'datadog/statsd' unless defined?(::Datadog::Statsd) Datadog::Statsd.new(default_hostname, default_port) ``` could try to create a `Statsd` instance BEFORE the class was fully defined, leading to `ArgumentError: wrong number of arguments (given 2, expected 0)` when calling `initialize`. This error stems from the custom `#initialize` method not being available yet, so Ruby falls back to `Object#initialize`, which does not take any argument. See also: * <https://slides.com/ianjo/spotting-unsafe-concurrent-ruby-patterns-fullstack-lx#/0/42/0> * <https://gitlab.com/ivoanjo/unsafe-concurrent-ruby-patterns/-/blob/master/background_require.rb> The fix in this case is to always call `require`. Calling `require` repeatedly has little impact -- a given file is only loaded by `require` the first time AND concurrent `require`s are protected -- only one thread will get to execute the require, will all others wait until it finishes. The follow example demonstrates this: * `lazy-require.rb`: ```ruby def try_require puts "#{Thread.current} Going to require" require_relative './lazy-require-sleep-1' puts "#{Thread.current} After require" end 2.times.map { Thread.new { try_require } }.map(&:join) try_require ``` * `lazy-require-sleep-1.rb`: ```ruby puts "#{Thread.current} Running require" sleep 1 puts "#{Thread.current} Require finished" ``` * Output: ``` $ ruby lazy-require.rb ``` Notice how two threads tried to require, but only one actually ran the code on the other file, while the other was blocked and only woke up after the `require` finished. Fixes #1306
If for some reason multiple tracers are initializing concurrently (see customer issue in #1306 or the flaky test in `tracer_integration_spec.rb` in the previous commit), multiple threads can try to run `default_statsd_client` concurrently. Because Ruby `require`s are not atomic (e.g. you can observe classes that are half-loaded), our conditional ```ruby require 'datadog/statsd' unless defined?(::Datadog::Statsd) Datadog::Statsd.new(default_hostname, default_port) ``` could try to create a `Statsd` instance BEFORE the class was fully defined, leading to `ArgumentError: wrong number of arguments (given 2, expected 0)` when calling `initialize`. This error stems from the custom `#initialize` method not being available yet, so Ruby falls back to `Object#initialize`, which does not take any argument. See also: * <https://slides.com/ianjo/spotting-unsafe-concurrent-ruby-patterns-fullstack-lx#/0/42/0> * <https://gitlab.com/ivoanjo/unsafe-concurrent-ruby-patterns/-/blob/master/background_require.rb> The fix in this case is to always call `require`. Calling `require` repeatedly has little impact -- a given file is only loaded by `require` the first time AND concurrent `require`s are protected -- only one thread will get to execute the require, will all others wait until it finishes. The follow example demonstrates this: * `lazy-require.rb`: ```ruby def try_require puts "#{Thread.current} Going to require" require_relative './lazy-require-sleep-1' puts "#{Thread.current} After require" end 2.times.map { Thread.new { try_require } }.map(&:join) try_require ``` * `lazy-require-sleep-1.rb`: ```ruby puts "#{Thread.current} Running require" sleep 1 puts "#{Thread.current} Require finished" ``` * Output: ``` $ ruby lazy-require.rb #<Thread:0x00007fc08b9081e0 lazy-require.rb:7 run> Going to require #<Thread:0x00007fc08b90be80 lazy-require.rb:7 run> Going to require #<Thread:0x00007fc08b9081e0 lazy-require.rb:7 run> Running require #<Thread:0x00007fc08b9081e0 lazy-require.rb:7 run> Require finished #<Thread:0x00007fc08b9081e0 lazy-require.rb:7 run> After require #<Thread:0x00007fc08b90be80 lazy-require.rb:7 run> After require #<Thread:0x00007fc08b85fa90 run> Going to require #<Thread:0x00007fc08b85fa90 run> After require ``` Notice how two threads tried to require, but only one actually ran the code on the other file, while the other was blocked and only woke up after the `require` finished. Fixes #1306
Hi guys, please help me to understand the issue we are having.
Lately we upgraded to rails 6 and ruby 2.7 and currently we are trying to resolve all deprecation messages and move to Zeitwerk.
The problem: On every deploy to Heroku some of our dynos crash with:
It could be Sidekiq and Sneakers but not a web dyno.
It seems like here:
dd-trace-rb/lib/ddtrace/metrics.rb
Line 16 in a770176
the problem is with initialising
default_statsd_client
After adding Statsd client with default values to the initialiser the dynos stopped crashing
But... We also upgraded the Airbrake gem and it added a mechanism that polls for remote configurations every x seconds. Once I disabled it, the crashes also stopped (without the mentioned fix).
Can you please elaborate on that error?
The text was updated successfully, but these errors were encountered: