Skip to content
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

Rake integration gets into an infinite loop if anything else overrides execute #556

Closed
DanielHeath opened this issue Aug 3, 2019 · 11 comments
Labels
bug Confirmed bug released This feature/bug fix has been released scheduled Work is starting on this feature/bug

Comments

@DanielHeath
Copy link

Description

I'm using https://github.com/honeycombio/beeline-ruby alongside bugsnag-ruby. They aren't getting along, though. The overrides of Rake::Task#execute are mutually recursing.

Having had a read of the relevant code on both sides (honeycomb and bugsnag), I think the issue is on the bugsnag-ruby side.

Stack trace

gems/bugsnag-6.11.1/lib/bugsnag/integrations/rake.rb:20:in `execute_with_bugsnag'
gems/honeycomb-beeline-1.0.0/lib/honeycomb/integrations/rake.rb:14:in `execute'
gems/bugsnag-6.11.1/lib/bugsnag/integrations/rake.rb:20:in `execute_with_bugsnag'
gems/honeycomb-beeline-1.0.0/lib/honeycomb/integrations/rake.rb:14:in `execute'
gems/bugsnag-6.11.1/lib/bugsnag/integrations/rake.rb:20:in `execute_with_bugsnag'
gems/honeycomb-beeline-1.0.0/lib/honeycomb/integrations/rake.rb:14:in `execute'

The issue is that honeycomb defines execute in a module, which it adds using prepend. It calls super, which (thanks to aliasing) is execute_with_bugsnag, which calls execute_without_bugsnag, which (also thanks to the aliasing in bugsnag-ruby) is the execute implementation defined by honeycomb.

This is complex, because bugsnag supports ruby < 2, which doesn't have prepend. Otherwise it'd an easy fix (do it the ruby 2 way with prepend).

I think changing to use a module for the extra behavior added to Rake::Task would solve this immediate issue, but not being able to use prepend means you wouldn't be instrumenting other modules mixed into Rake::Task.

@abigailbramble abigailbramble added bug Confirmed bug backlog We hope to fix this feature/bug in the future labels Aug 6, 2019
@tobyhs
Copy link
Contributor

tobyhs commented Aug 7, 2019

Hi @DanielHeath

After trying a few things, it looks like adding a require: false in the Gemfile (like gem 'honeycomb-beeline', '1.0.0', require: false) avoids the issue, and you can require 'honeycomb-beeline' somewhere later like in config/initializers/honeycomb.rb. Would this be an acceptable workaround for now?

@DanielHeath
Copy link
Author

DanielHeath commented Aug 7, 2019 via email

@DanielHeath
Copy link
Author

DanielHeath commented Aug 7, 2019 via email

@tobyhs
Copy link
Contributor

tobyhs commented Aug 7, 2019

I made an update to my original comment that may have been missed (about requiring 'honeycomb-beeline' in an initializer file).

I have the following, and it doesn't encounter an infinite loop/recursion:

$ grep 'bugsnag\|honeycomb' Gemfile
gem 'bugsnag', '6.11.1'
gem 'honeycomb-beeline', '1.0.0', require: false
$ cat config/initializers/honeycomb.rb
require 'honeycomb-beeline'

Honeycomb.configure do |config|
  config.write_key = "abc123"
  config.dataset = "rails"
  config.notification_events = %w[
    sql.active_record
    render_template.action_view
    render_partial.action_view
    render_collection.action_view
    process_action.action_controller
    send_file.action_controller
    send_data.action_controller
    deliver.action_mailer
  ].freeze
end
$ cat lib/tasks/mytest.rake
namespace :mytest do
  desc 'Testing'
  task test: :environment do
    puts Rake::Task.instance_method(:execute_with_bugsnag)
    puts Rake::Task.ancestors.inspect
  end
end
$ be rake mytest:test
#<UnboundMethod: Rake::Task#execute_with_bugsnag>
[Honeycomb::Rake::Task, Rake::Task, ActiveSupport::ToJsonWithActiveSupportEncoder, Object, PP::ObjectMixin, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, Kernel, BasicObject]

@DanielHeath
Copy link
Author

Gave that a try, but there's a lot of situations in my app where a rake task is invoked after the environment has already been loaded, which still hits this error.

tobyhs pushed a commit that referenced this issue Aug 14, 2019
This might fix #556

If another gem uses `Module#prepend` on `Rake::Task`, then it can lead
to infinite mutual recursion if we use alias_method to monkey patch the
`#execute` method.
tobyhs pushed a commit that referenced this issue Aug 14, 2019
This might fix #556

If another gem uses `Module#prepend` on `Rake::Task`, then it can lead
to infinite mutual recursion if we use alias_method to monkey patch the
`#execute` method.
tobyhs pushed a commit that referenced this issue Aug 14, 2019
This might fix #556

If another gem uses `Module#prepend` on `Rake::Task`, then it can lead
to infinite mutual recursion if we use alias_method to monkey patch the
`#execute` method.
@tobyhs
Copy link
Contributor

tobyhs commented Aug 14, 2019

Can you give the branch at https://github.com/bugsnag/bugsnag-ruby/tree/tobyhs/rake_with_module_prepend a try to see if it works for your app?

@DanielHeath
Copy link
Author

Tried switching to that branch, and the mutual recursion is gone :) thanks.

@tobyhs
Copy link
Contributor

tobyhs commented Aug 16, 2019

I have merged #559 (into the next branch) and plan on deleting the tobyhs/rake_with_module_prepend branch soon (to keep things tidy), so if you were relying on that branch and can't wait until the next bugsnag-ruby release for the fix, I would advise using the next branch at commit 1446466

tomlongridge pushed a commit that referenced this issue Aug 23, 2019
This might fix #556

If another gem uses `Module#prepend` on `Rake::Task`, then it can lead
to infinite mutual recursion if we use alias_method to monkey patch the
`#execute` method.
@abigailbramble abigailbramble added released This feature/bug fix has been released and removed backlog We hope to fix this feature/bug in the future labels Sep 2, 2019
@abigailbramble
Copy link

@danarnold
Copy link

danarnold commented Feb 25, 2020

It looks like the code introduced to fix this actually ends up breaking compatibility with other gems, such as elastic/apm-agent-ruby. This blog post from New Relic explains the incompatibility in some detail. In our application, we tried to get bugsnag to load after the elastic-apm gem, but we weren't able to get it working, and our workaround for now was to use a version of the bugsnag gem earlier than v6.12.0. There's also some discussion on this issue on the elastic-apm gem's tracker.

I'm not sure what the best solution is here, as this seems to be a language issue that has brought about these incompatibilities in libraries that implement things differently.

@xljones
Copy link

xljones commented Feb 27, 2020

Hey @danarnold, thanks for bringing this to our attention. Much like the discussions in apm-agent-ruby, we're having the similar ones here. There's going to be some incompatibilities depending on what libraries the user is using in their project, and whether they have interfering methods.

For the benefit of future readers; the Ruby gems prior to v6.12.0, will be compatible with other libraries that use alias_method chains as they won't use Module#prepend, so I would suggest continuing with this method for now is a sensible approach. --Xander

@bugsnagbot bugsnagbot added the scheduled Work is starting on this feature/bug label Mar 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Confirmed bug released This feature/bug fix has been released scheduled Work is starting on this feature/bug
Projects
None yet
Development

No branches or pull requests

6 participants