getsentry / sentry-ruby

Sentry SDK for Ruby
https://sentry.io/for/ruby
MIT License
927 stars 493 forks source link

Sentry/VCR `Net::HTTP` monkey patches collision #2262

Closed viralpraxis closed 5 months ago

viralpraxis commented 6 months ago

Issue Description

We use Sentry in our RoR application alongside with VCR. For some reason, in order to make a new cassette within our RSpec testing suite we have to temporary disable Sentry initialization to make it working -- otherwise Sentry and VCR seem to get into a recursive loop (see details below) and application fails with SystemStackError exception

Reproduction Steps

  1. We configure Sentry like this

Sentry.init do |sentry_config|
  sentry_config.dsn = config.fetch(:dsn)
end
  1. We try to run spec with VCR like this (cassette does not exist yet)
VCR.use_cassette("cassette-name") do
  payment_system_order.initialize_payment # this call makes an HTTP request
end
  1. Somewhere after Net::HTTP takes execution control an SystemStackError: stack level too deep occurs with repeated backtrace chunks:
    [9636] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sniffer-0.5.0/lib/sniffer/adapters/net_http_adapter.rb:13:in `request_with_sniffer'",
    [9637] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/net/http.rb:40:in `block in request'",
    [9638] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/hub.rb:102:in `with_child_span'",
    [9639] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry-ruby.rb:478:in `with_child_span'",
    [9640] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/net/http.rb:33:in `request'",
    [9641] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sniffer-0.5.0/lib/sniffer/adapters/net_http_adapter.rb:14:in `block in request_with_sniffer'",
    [9642] "~/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/benchmark.rb:313:in `realtime'",
    [9643] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sniffer-0.5.0/lib/sniffer/adapters/net_http_adapter.rb:13:in `request_with_sniffer'",
    [9644] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/net/http.rb:40:in `block in request'",
    [9645] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/hub.rb:102:in `with_child_span'",
    [9646] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry-ruby.rb:478:in `with_child_span'",
    [9647] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/net/http.rb:33:in `request'",
    [9648] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sniffer-0.5.0/lib/sniffer/adapters/net_http_adapter.rb:14:in `block in request_with_sniffer'",
    [9649] "~/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/benchmark.rb:313:in `realtime'",
    [9650] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sniffer-0.5.0/lib/sniffer/adapters/net_http_adapter.rb:13:in `request_with_sniffer'",
    [9651] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/net/http.rb:40:in `block in request'",
    [9652] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/hub.rb:102:in `with_child_span'",
    [9653] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry-ruby.rb:478:in `with_child_span'",
    [9654] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sentry-ruby-5.16.1/lib/sentry/net/http.rb:33:in `request'",
    [9655] "~/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/sniffer-0.5.0/lib/sniffer/adapters/net_http_adapter.rb:14:in `block in request_with_sniffer'",

Ruby: MRI 3.3.0 capistrano-sentry (0.4.2) sentry-rails (5.16.1) sentry-ruby (5.16.1) sentry-sidekiq (5.16.1)

sniffer (0.5.0) vcr (6.2.0)

Temporary removing sentry initializer fixes the issue

Expected Behavior

I expect no exception to occur

Actual Behavior

Ruby Version

3.3.0

SDK Version

5.16.1

Integration and Its Version

Rails 7.1.3.2 sniffer (0.5.0) vcr (6.2.0)

Sentry Config

Sentry.init do |sentry_config|
  sentry_config.dsn = config.fetch(:dsn)
end
sl0thentr0py commented 6 months ago

@viralpraxis thanks for the report, will investigate

st0012 commented 5 months ago

From the stacktrace, it looks like the collision is caused by the sniffer gem, not VCR. And the reason is that sniffer by default uses alias_method to patch Net::HTTP, which is a deprecated approach and it's not compatible with prepend patches that this SDK (and many other gems) use.

Fortunately, sniffer provides an option to load the patches with prepend. Can you try applying them and let us know if that resolves the issue?

viralpraxis commented 5 months ago

@st0012 Yep, applying gem "sniffer", require: %w[all_prepend sniffer] fixed the issue. I clearly saw that conflict happens at sniffer dependency of VCR, but didn't bother to read its docs.. Thank you!