diff --git a/Gemfile b/Gemfile index cc2d17b5b7..78a5826b14 100644 --- a/Gemfile +++ b/Gemfile @@ -9,3 +9,5 @@ gem 'sidekiq' gem 'sucker_punch' gem 'delayed_job' gem 'queue_classic' +gem 'sneakers', '0.1.1.pre' +gem 'que' diff --git a/Gemfile.lock b/Gemfile.lock index 17f0d032e3..6f1ac23a94 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,7 +20,10 @@ GEM minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) + amq-protocol (1.9.2) builder (3.2.2) + bunny (1.1.9) + amq-protocol (>= 1.9.2) celluloid (0.15.2) timers (~> 1.1.0) connection_pool (2.0.0) @@ -32,6 +35,7 @@ GEM mono_logger (1.1.0) multi_json (1.9.3) pg (0.17.1) + que (0.7.3) queue_classic (2.2.3) pg (~> 0.17.0) rack (1.5.2) @@ -53,18 +57,28 @@ GEM rufus-scheduler (~> 2.0) rufus-scheduler (2.0.24) tzinfo (>= 0.3.22) + serverengine (1.5.7) + sigdump (~> 0.2.2) sidekiq (3.0.2) celluloid (>= 0.15.2) connection_pool (>= 2.0.0) json redis (>= 3.0.6) redis-namespace (>= 1.3.1) + sigdump (0.2.2) sinatra (1.4.4) rack (~> 1.4) rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) + sneakers (0.1.1.pre) + bunny (~> 1.1.3) + serverengine + thor + thread sucker_punch (1.0.5) celluloid (~> 0.15.2) + thor (0.19.1) + thread (0.1.4) thread_safe (0.3.3) tilt (1.4.1) timers (1.1.0) @@ -79,9 +93,11 @@ PLATFORMS DEPENDENCIES activejob! delayed_job + que queue_classic rake resque resque-scheduler sidekiq + sneakers (= 0.1.1.pre) sucker_punch diff --git a/README.md b/README.md index a0e5f01e29..1be57a1808 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ of the request-response cycle, so the user doesn't have to wait on it. The main point is to ensure that all Rails apps will have a job infrastructure in place, even if it's in the form of an "immediate runner". We can then have framework features and other gems build on top of that, without having to worry -about API differences between Delayed Job and Resque. Picking your queuing +about API differences between Delayed Job and Resque. Picking your queuing backend becomes more of an operational concern, then. And you'll be able to switch between them without having to rewrite your jobs. @@ -24,13 +24,16 @@ Set the queue adapter for Active Job: ``` ruby ActiveJob::Base.queue_adapter = :inline # default queue adapter -# Adapters currently supported: :resque, :sidekiq, :sucker_punch, :delayed_job +# Adapters currently supported: :delayed_job, :que, :queue_classic, :resque, +# :sidekiq, :sneakers, :sucker_punch ``` Declare a job like so: ```ruby class MyJob < ActiveJob::Base + queue_as :my_jobs + def perform(record) record.do_work end @@ -79,15 +82,17 @@ by default has been mixed into Active Record classes. We currently have adapters for: -* Resque 1.x -* Sidekiq -* Sucker Punch -* Delayed Job -* QueueClassic +* [Delayed Job](https://github.com/collectiveidea/delayed_job) +* [Que](https://github.com/chanks/que) +* [QueueClassic](https://github.com/ryandotsmith/queue_classic) +* [Resque 1.x](https://github.com/resque/resque) +* [Sidekiq](https://github.com/mperham/sidekiq) +* [Sneakers](https://github.com/jondot/sneakers) +* [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) We would like to have adapters for: -* Sneakers +* [Resque 2.x](https://github.com/resque/resque) (see [#7](https://github.com/rails/activejob/issues/7)) ## Under development as a gem, targeted for Rails inclusion diff --git a/Rakefile b/Rakefile index 2b8b81248d..933c049a68 100644 --- a/Rakefile +++ b/Rakefile @@ -20,11 +20,11 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job test_queue_classic) + tasks = %w(test_inline test_delayed_job test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch) run_without_aborting(*tasks) end -%w(inline resque sidekiq sucker_punch delayed_job queue_classic).each do |adapter| +%w(inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 77b929d4af..3d16f38275 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,11 +1,13 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' require 'active_job/enqueuing' +require 'active_job/logging' module ActiveJob class Base extend QueueAdapter extend QueueName extend Enqueuing + extend Logging end -end \ No newline at end of file +end diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index a5a50d69db..b2d142ee96 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -11,6 +11,7 @@ module ActiveJob # The return value is adapter-specific and may change in a future # ActiveJob release. def enqueue(*args) + ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, params: args queue_adapter.queue self, *Parameters.serialize(args) end diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb new file mode 100644 index 0000000000..31c61a6068 --- /dev/null +++ b/lib/active_job/log_subscriber.rb @@ -0,0 +1,18 @@ +module ActiveJob + class LogSubscriber < ActiveSupport::LogSubscriber + def enqueue(event) + payload = event.payload + params = payload[:params] + adapter = payload[:adapter] + job = payload[:job] + + info "ActiveJob enqueued to #{adapter.name.demodulize} job #{job.name}: #{params.inspect}" + end + + def logger + ActiveJob::Base.logger + end + end +end + +ActiveJob::LogSubscriber.attach_to :active_job diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb new file mode 100644 index 0000000000..0e994a8f54 --- /dev/null +++ b/lib/active_job/logging.rb @@ -0,0 +1,7 @@ +require 'active_job/log_subscriber' + +module ActiveJob + module Logging + mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } + end +end diff --git a/lib/active_job/queue_adapter.rb b/lib/active_job/queue_adapter.rb index 2033f6fe56..8f2f8b86ea 100644 --- a/lib/active_job/queue_adapter.rb +++ b/lib/active_job/queue_adapter.rb @@ -6,14 +6,13 @@ module ActiveJob mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } def queue_adapter=(name_or_adapter) - case name_or_adapter - when Symbol, String - adapter = load_adapter(name_or_adapter) - else - adapter = name_or_adapter - end - - @@queue_adapter = adapter + @@queue_adapter = \ + case name_or_adapter + when Symbol, String + load_adapter(name_or_adapter) + when Class + name_or_adapter + end end private diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb new file mode 100644 index 0000000000..6750882b91 --- /dev/null +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -0,0 +1,19 @@ +require 'que' + +module ActiveJob + module QueueAdapters + class QueAdapter + class << self + def queue(job, *args) + JobWrapper.enqueue job, *args, queue: job.queue_name + end + end + + class JobWrapper < Que::Job + def run(job, *args) + job.new.perform *Parameters.deserialize(args) + end + end + end + end +end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index 38c04ca5c9..d0e2e1aa22 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -5,8 +5,7 @@ module ActiveJob class QueueClassicAdapter class << self def queue(job, *args) - qc_queue = QC::Queue.new(job.queue_name) - qc_queue.enqueue("ActiveJob::QueueAdapters::QueueClassicAdapter::JobWrapper.perform", job, *args) + QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job, *args) end def queue_at(job, timestamp, *args) diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb new file mode 100644 index 0000000000..c6dbfa75bf --- /dev/null +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -0,0 +1,23 @@ +require 'sneakers' + +module ActiveJob + module QueueAdapters + class SneakersAdapter + class << self + def queue(job, *args) + JobWrapper.enqueue([job, *args]) + end + end + + class JobWrapper + include Sneakers::Worker + + self.from_queue("queue", {}) + + def work(job, *args) + job.new.perform *Parameters.deserialize(args) + end + end + end + end +end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 63019e37c0..e483c0844b 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -22,8 +22,8 @@ module ActiveJob class JobWrapper include SuckerPunch::Job - def perform(job_name, *args) - job_name.new.perform *Parameters.deserialize(args) + def perform(job, *args) + job.new.perform *Parameters.deserialize(args) end def later(sec, job_name, *args) diff --git a/test/adapters/que.rb b/test/adapters/que.rb new file mode 100644 index 0000000000..640061bf54 --- /dev/null +++ b/test/adapters/que.rb @@ -0,0 +1,2 @@ +ActiveJob::Base.queue_adapter = :que +Que.mode = :sync diff --git a/test/adapters/sneakers.rb b/test/adapters/sneakers.rb new file mode 100644 index 0000000000..204166a700 --- /dev/null +++ b/test/adapters/sneakers.rb @@ -0,0 +1,2 @@ +require 'support/sneakers/inline' +ActiveJob::Base.queue_adapter = :sneakers diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index 05efdc33e9..1a08fa2617 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -1,36 +1,46 @@ require 'helper' class AdapterTest < ActiveSupport::TestCase - def setup - @old_adapter = ActiveJob::Base.queue_adapter - end + setup { @old_adapter = ActiveJob::Base.queue_adapter } + teardown { ActiveJob::Base.queue_adapter = @old_adapter } test 'should load inline adapter' do ActiveJob::Base.queue_adapter = :inline assert_equal ActiveJob::QueueAdapters::InlineAdapter, ActiveJob::Base.queue_adapter end - test 'should load resque adapter' do - ActiveJob::Base.queue_adapter = :resque - assert_equal ActiveJob::QueueAdapters::ResqueAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load sidekiq adapter' do - ActiveJob::Base.queue_adapter = :sidekiq - assert_equal ActiveJob::QueueAdapters::SidekiqAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load sucker punch adapter' do - ActiveJob::Base.queue_adapter = :sucker_punch - assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load delayed_job adapter' do + test 'should load Delayed Job adapter' do ActiveJob::Base.queue_adapter = :delayed_job assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter end - def teardown - ActiveJob::Base.queue_adapter = @old_adapter + test 'should load Que adapter' do + ActiveJob::Base.queue_adapter = :que + assert_equal ActiveJob::QueueAdapters::QueAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Queue Classic adapter' do + ActiveJob::Base.queue_adapter = :queue_classic + assert_equal ActiveJob::QueueAdapters::QueueClassicAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Resque adapter' do + ActiveJob::Base.queue_adapter = :resque + assert_equal ActiveJob::QueueAdapters::ResqueAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Sidekiq adapter' do + ActiveJob::Base.queue_adapter = :sidekiq + assert_equal ActiveJob::QueueAdapters::SidekiqAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Sucker Punch adapter' do + ActiveJob::Base.queue_adapter = :sucker_punch + assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Sneakers adapter' do + ActiveJob::Base.queue_adapter = :sneakers + assert_equal ActiveJob::QueueAdapters::SneakersAdapter, ActiveJob::Base.queue_adapter end end diff --git a/test/support/sneakers/inline.rb b/test/support/sneakers/inline.rb new file mode 100644 index 0000000000..16d9b830fa --- /dev/null +++ b/test/support/sneakers/inline.rb @@ -0,0 +1,12 @@ +require 'sneakers' + +module Sneakers + module Worker + module ClassMethods + def enqueue(msg) + worker = self.new(nil, nil, {}) + worker.work(*msg) + end + end + end +end