-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Upstream Active Job adapter #1906
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
base: master
Are you sure you want to change the base?
Conversation
Rails wants to slim down the number of adapters it ships with. This approach is not dissimilar from que-rb/que#403, but we have included all of the tests from Active Job to ensure it remains stable. Not all of the tests are necessary, we can trim down the tests for non-resque adapters for example. But I wanted to start with a clean base, and find a pattern that can be applied to all of the adapters we want to extract. The reason for the extension and `const_remove` is that the class name serialized for the job cannot change, otherwise we risk losing jobs. In the future, after the adapter is removed downstream, you should be able to version guard that part at least. And eventually if you so choose, rename the constant, assuming that you give users ample time to migrate their queues but that seems unnecessary.
@zzak Thanks for taking a crack at this. Admittedly, even though I use a lot of Rails and Resque in production, none of them use ActiveJob... never felt the need for the abstraction layer when we could just go direct to Resque. With that in mind, can you explain this bit?
I gotta spend some time noodling the ramifications here and just make sure that by merging this in and okaying the downstream removal, we're not going to cause some pain for folks when they do an update. Probably wind up being a major Resque release to the say the least. |
@PatrickTulskie Thanks for your reply! The goal would be to deprecate the adapter in Rails and remove it in the next release, given the upstream (resque in this case) is willing to take on the adapter. Then Rails can focus on a much leaner set of adapters to maintain and fewer dependencies which speeds things up like CI and save contributors time. I've set things up to run on Rails main, so we can still know about breaking changes early, ideally there are none for the adapters. They are stable and that is another reason for this separation. But I understand if you're not interested, have no use for Active Job in your work. I am happy to help maintain it if you'd like to add me as a contributor. I can also slim things down if this adds too much overhead, like to CI and such.
This is exactly my goal, to avoid making any breaking changes other than a version bump in a lock file. You can see in sidekiq/sidekiq#6434 where I've demonstrated this. To explain that comment about the constant naming, here is an example of two jobs queued up using different classes:
And the result of trying to process that job with an unpatched version of the adapter:
So the naming of the constant matters here if we are to avoid any breaking change. This way a Rails app can update to a version of Resque with the adapter and continue processing jobs happily, or in the middle of a roll out where both versions are running, and same for if a rollback is made but the queue still has new jobs in it. That should all be fine here. |
@zzak Awesome thanks for that explanation. Also yeah, totally understand where DHH and the team are coming from and I think bringing the adapter into resque is the right thing to do here, especially given most people probably also use it with Rails. I just wanted to get some more information before moving forward and make sure we're all on the same page. At my day job, I've been asking around and found a single app that is using the Resque adapter with ActiveJob so at least I have a real world app to test with before committing to this. Probably will prep an internal build in the next week or so, and if our cut over looks good I'll feel pretty good about merging this in. I've been going back and forth and I think this is probably major release worthy. In preparation for that, I also started going through other PRs that have been languishing and trying to see if there is anything else that should get done before that major release. I'll try to get it done before Rails 8 but I can't make any promises at the moment. |
lib/resque/active_job_extension.rb
Outdated
@@ -0,0 +1,54 @@ | |||
# frozen_string_literal: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this just be in lib/active_job/queue_adapters/resque_adapter.rb
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Based on the class name and namespace, it should probably live there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just gave this a quick pass and I agree with @xjunior that the actual adapter probably needs to move. The rest looks good to me though.
lib/resque/active_job_extension.rb
Outdated
@@ -0,0 +1,54 @@ | |||
# frozen_string_literal: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8000Agreed. Based on the class name and namespace, it should probably live there.
Similar to sidekiq/sidekiq#6477. This resulted in a circular require, which will fail in Rails CI. ``` CI=1 bundle exec rake test:integration:resque /home/zzak/.rbenv/versions/3.3.5/bin/ruby -w -I"lib:test" /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/rake-13.2.1/lib/rake/rake_test_loader.rb "test/integration/queuing_test.rb" Using resque /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75: warning: /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75: warning: loading in progress, circular require considered harmful - /home/zzak/code/rails/activejob/lib/active_job/queue_adapters/resque_adapter.rb from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/rake-13.2.1/lib/rake/rake_test_loader.rb:6:in `<main>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/rake-13.2.1/lib/rake/rake_test_loader.rb:6:in `select' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/rake-13.2.1/lib/rake/rake_test_loader.rb:21:in `block in <main>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in replace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' from /home/zzak/code/rails/activejob/test/integration/queuing_test.rb:3:in `<top (required)>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in replace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' from /home/zzak/code/rails/activejob/test/helper.rb:13:in `<top (required)>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in replace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' from /home/zzak/code/rails/activejob/test/support/integration/helper.rb:16:in `<top (required)>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in replace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' from /tmp/d20241101-2349270-pkyv1o/dummy/config/environment.rb:5:in `<top (required)>' from /home/zzak/code/rails/railties/lib/rails/application.rb:440:in `initialize!' from /home/zzak/code/rails/railties/lib/rails/initializable.rb:60:in `run_initializers' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:208:in `tsort_each' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:229:in `tsort_each' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:350:in `each_strongly_connected_component' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:350:in `call' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:350:in `each' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:352:in `block in each_strongly_connected_component' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:418:in `each_strongly_connected_component_from' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:418:in `call' from /home/zzak/code/rails/railties/lib/rails/initializable.rb:50:in `tsort_each_child' from /home/zzak/code/rails/railties/lib/rails/initializable.rb:50:in `each' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:424:in `block in each_strongly_connected_component_from' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:434:in `each_strongly_connected_component_from' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:425:in `block (2 levels) in each_strongly_connected_component_from' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:353:in `block (2 levels) in each_strongly_connected_component' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/tsort.rb:231:in `block in tsort_each' from /home/zzak/code/rails/railties/lib/rails/initializable.rb:61:in `block in run_initializers' from /home/zzak/code/rails/railties/lib/rails/initializable.rb:32:in `run' from /home/zzak/code/rails/railties/lib/rails/initializable.rb:32:in `instance_exec' from /home/zzak/code/rails/railties/lib/rails/engine.rb:645:in `block in <class:Engine>' from /home/zzak/code/rails/railties/lib/rails/engine.rb:645:in `each' from /home/zzak/code/rails/railties/lib/rails/engine.rb:646:in `block (2 levels) in <class:Engine>' from /home/zzak/code/rails/railties/lib/rails/engine.rb:692:in `load_config_initializer' from /home/zzak/code/rails/activesupport/lib/active_support/notifications.rb:212:in `instrument' from /home/zzak/code/rails/railties/lib/rails/engine.rb:693:in `block in load_config_initializer' from /home/zzak/code/rails/railties/lib/rails/engine.rb:693:in `load' from /tmp/d20241101-2349270-pkyv1o/dummy/config/initializers/activejob.rb:2:in `<main>' from /home/zzak/code/rails/activejob/test/support/integration/adapters/resque.rb:5:in `setup' from /home/zzak/code/rails/activejob/lib/active_job/queue_adapter.rb:52:in `queue_adapter=' from /home/zzak/code/rails/activejob/lib/active_job/queue_adapters.rb:136:in `lookup' from /home/zzak/code/rails/activejob/lib/active_job/queue_adapters.rb:136:in `const_get' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/zeitwerk-2.7.1/lib/zeitwerk/core_ext/kernel.rb:34:in `require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/bootsnap-1.18.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in repl 8000 ace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' from /home/zzak/code/rails/activejob/lib/active_job/queue_adapters/resque_adapter.rb:3:in `<main>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/zeitwerk-2.7.1/lib/zeitwerk/core_ext/kernel.rb:34:in `require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/bootsnap-1.18.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in replace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' from /home/zzak/code/resque/lib/resque.rb:27:in `<main>' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/zeitwerk-2.7.1/lib/zeitwerk/core_ext/kernel.rb:34:in `require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/bootsnap-1.18.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `block (2 levels) in replace_require' from /home/zzak/.rbenv/versions/3.3.5/lib/ruby/3.3.0/bundled_gems.rb:75:in `require' ``` Interestingly, I started by working on resque/resque-scheduler#795, but after this change that fix no longer seems necessary. Although maybe safe to include anyways, and that PR also fixed the CI.
@PatrickTulskie Thanks for the review and your patience, I made some time to update the file path, fix a circular require warning, and get the build 🟢 Happy to squash the commits if you prefer that, but wanted to leave them so you can review each one if that's helpful. |
lib/resque/railtie.rb
Outdated
initializer "resque.active_job" do | ||
ActiveSupport.on_load(:active_job) do | ||
require "active_job/queue_adapters/resque_adapter" | ||
ActiveJob::Base.queue_adapter = :resque |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should do this. I would rather leave this as an application decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a maintainer, but left a comment. I feel like making that decision to the application can lead to unexpected behavior on the application. For instance, what if resque
is brought in by a third-party dep and that takes it over the queue_adapter
?
Furthermore, the adapter is more often than not managed and switched depending on the runtime environment (test
for tests, async
or inline
for development, resque
for production).
|
Fixes #1905
Rails wants to slim down the number of adapters it ships with.
This approach is not dissimilar from que-rb/que#403, but we have included all of the tests from Active Job to ensure it remains stable.
Not all of the tests are necessary, we can trim down the tests for non-resque adapters for example. But I wanted to start with a clean base, and find a pattern that can be applied to all of the adapters we want to extract.
The reason for the extension and
const_remove
is that the class name serialized for the job cannot change, otherwise we risk losing jobs.In the future, after the adapter is removed downstream, you should be able to version guard that part at least. And eventually if you so choose, rename the constant, assuming that you give users ample time to migrate their queues but that seems unnecessary.
/cc rails/rails#52929