activejob 4.2.0.beta1 → 4.2.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,15 +4,29 @@ require 'active_job/arguments'
4
4
  module ActiveJob
5
5
  module Execution
6
6
  extend ActiveSupport::Concern
7
+ include ActiveSupport::Rescuable
7
8
 
8
- included do
9
- include ActiveSupport::Rescuable
10
- end
9
+ module ClassMethods
10
+ # Performs the job immediately.
11
+ #
12
+ # MyJob.perform_now("mike")
13
+ #
14
+ def perform_now(*args)
15
+ job_or_instantiate(*args).perform_now
16
+ end
11
17
 
12
- def execute(job_id, *serialized_args)
13
- self.job_id = job_id
14
- self.arguments = deserialize_arguments(serialized_args)
18
+ def execute(job_data) #:nodoc:
19
+ job = deserialize(job_data)
20
+ job.perform_now
21
+ end
22
+ end
15
23
 
24
+ # Performs the job immediately. The job is not sent to the queueing adapter
25
+ # but directly executed by blocking the execution of others until it's finished.
26
+ #
27
+ # MyJob.new(*args).perform_now
28
+ def perform_now
29
+ deserialize_arguments_if_needed
16
30
  run_callbacks :perform do
17
31
  perform(*arguments)
18
32
  end
@@ -23,11 +37,5 @@ module ActiveJob
23
37
  def perform(*)
24
38
  fail NotImplementedError
25
39
  end
26
-
27
- private
28
- def deserialize_arguments(serialized_args)
29
- Arguments.deserialize(serialized_args)
30
- end
31
-
32
40
  end
33
41
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveJob
2
- # Returns the version of the currently loaded ActiveJob as a <tt>Gem::Version</tt>
2
+ # Returns the version of the currently loaded Active Job as a <tt>Gem::Version</tt>
3
3
  def self.gem_version
4
4
  Gem::Version.new VERSION::STRING
5
5
  end
@@ -8,7 +8,7 @@ module ActiveJob
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
10
  TINY = 0
11
- PRE = "beta1"
11
+ PRE = "beta2"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -17,7 +17,7 @@ module ActiveJob
17
17
 
18
18
  around_perform do |job, block, _|
19
19
  tag_logger(job.class.name, job.job_id) do
20
- payload = {adapter: job.class.queue_adapter, job: job.class, args: job.arguments}
20
+ payload = {adapter: job.class.queue_adapter, job: job}
21
21
  ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup)
22
22
  ActiveSupport::Notifications.instrument("perform.active_job", payload) do
23
23
  block.call
@@ -26,12 +26,12 @@ module ActiveJob
26
26
  end
27
27
 
28
28
  before_enqueue do |job|
29
- if job.enqueued_at
29
+ if job.scheduled_at
30
30
  ActiveSupport::Notifications.instrument "enqueue_at.active_job",
31
- adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments, timestamp: job.enqueued_at
31
+ adapter: job.class.queue_adapter, job: job
32
32
  else
33
33
  ActiveSupport::Notifications.instrument "enqueue.active_job",
34
- adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments
34
+ adapter: job.class.queue_adapter, job: job
35
35
  end
36
36
  end
37
37
  end
@@ -50,21 +50,33 @@ module ActiveJob
50
50
  logger.formatter.current_tags.include?("ActiveJob")
51
51
  end
52
52
 
53
- class LogSubscriber < ActiveSupport::LogSubscriber
53
+ class LogSubscriber < ActiveSupport::LogSubscriber #:nodoc:
54
54
  def enqueue(event)
55
- info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)}" + args_info(event)
55
+ info do
56
+ job = event.payload[:job]
57
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)}" + args_info(job)
58
+ end
56
59
  end
57
60
 
58
61
  def enqueue_at(event)
59
- info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event)
62
+ info do
63
+ job = event.payload[:job]
64
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
65
+ end
60
66
  end
61
67
 
62
68
  def perform_start(event)
63
- info "Performing #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event)
69
+ info do
70
+ job = event.payload[:job]
71
+ "Performing #{job.class.name} from #{queue_name(event)}" + args_info(job)
72
+ end
64
73
  end
65
74
 
66
75
  def perform(event)
67
- info "Performed #{event.payload[:job].name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms"
76
+ info do
77
+ job = event.payload[:job]
78
+ "Performed #{job.class.name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms"
79
+ end
68
80
  end
69
81
 
70
82
  private
@@ -72,12 +84,12 @@ module ActiveJob
72
84
  event.payload[:adapter].name.demodulize.remove('Adapter') + "(#{event.payload[:job].queue_name})"
73
85
  end
74
86
 
75
- def args_info(event)
76
- event.payload[:args].any? ? " with arguments: #{event.payload[:args].map(&:inspect).join(", ")}" : ""
87
+ def args_info(job)
88
+ job.arguments.any? ? " with arguments: #{job.arguments.map(&:inspect).join(", ")}" : ""
77
89
  end
78
90
 
79
- def enqueued_at(event)
80
- Time.at(event.payload[:timestamp]).utc
91
+ def scheduled_at(event)
92
+ Time.at(event.payload[:job].scheduled_at).utc
81
93
  end
82
94
 
83
95
  def logger
@@ -3,22 +3,27 @@ require 'active_support/core_ext/string/inflections'
3
3
 
4
4
  module ActiveJob
5
5
  module QueueAdapter
6
- mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
6
+ extend ActiveSupport::Concern
7
7
 
8
- def queue_adapter=(name_or_adapter)
9
- @@queue_adapter = \
10
- case name_or_adapter
11
- when Symbol, String
12
- load_adapter(name_or_adapter)
13
- when Class
14
- name_or_adapter
15
- end
16
- end
8
+ module ClassMethods
9
+ mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
17
10
 
18
- private
19
- def load_adapter(name)
20
- require "active_job/queue_adapters/#{name}_adapter"
21
- "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
11
+ def queue_adapter=(name_or_adapter)
12
+ @@queue_adapter = \
13
+ case name_or_adapter
14
+ when :test
15
+ ActiveJob::QueueAdapters::TestAdapter.new
16
+ when Symbol, String
17
+ load_adapter(name_or_adapter)
18
+ when Class
19
+ name_or_adapter
20
+ end
22
21
  end
22
+
23
+ private
24
+ def load_adapter(name)
25
+ "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
26
+ end
27
+ end
23
28
  end
24
29
  end
@@ -0,0 +1,51 @@
1
+ module ActiveJob
2
+ # == Active Job adapters
3
+ #
4
+ # Active Job has adapters for the following queueing backends:
5
+ #
6
+ # * {Backburner}[https://github.com/nesquena/backburner]
7
+ # * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
8
+ # * {Qu}[https://github.com/bkeepers/qu]
9
+ # * {Que}[https://github.com/chanks/que]
10
+ # * {QueueClassic 2.x}[https://github.com/ryandotsmith/queue_classic/tree/v2.2.3]
11
+ # * {Resque 1.x}[https://github.com/resque/resque/tree/1-x-stable]
12
+ # * {Sidekiq}[http://sidekiq.org]
13
+ # * {Sneakers}[https://github.com/jondot/sneakers]
14
+ # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
15
+ #
16
+ # #### Backends Features
17
+ #
18
+ # | | Async | Queues | Delayed | Priorities | Timeout | Retries |
19
+ # |-------------------|-------|--------|-----------|------------|---------|---------|
20
+ # | Backburner | Yes | Yes | Yes | Yes | Job | Global |
21
+ # | Delayed Job | Yes | Yes | Yes | Job | Global | Global |
22
+ # | Que | Yes | Yes | Yes | Job | No | Job |
23
+ # | Queue Classic | Yes | Yes | No* | No | No | No |
24
+ # | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
25
+ # | Sidekiq | Yes | Yes | Yes | Queue | No | Job |
26
+ # | Sneakers | Yes | Yes | No | Queue | Queue | No |
27
+ # | Sucker Punch | Yes | Yes | No | No | No | No |
28
+ # | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A |
29
+ # | Active Job | Yes | Yes | Yes | No | No | No |
30
+ #
31
+ # NOTE:
32
+ # Queue Classic does not support Job scheduling. However you can implement this
33
+ # yourself or you can use the queue_classic-later gem. See the documentation for
34
+ # ActiveJob::QueueAdapters::QueueClassicAdapter.
35
+ #
36
+ module QueueAdapters
37
+ extend ActiveSupport::Autoload
38
+
39
+ autoload :InlineAdapter
40
+ autoload :BackburnerAdapter
41
+ autoload :DelayedJobAdapter
42
+ autoload :QuAdapter
43
+ autoload :QueAdapter
44
+ autoload :QueueClassicAdapter
45
+ autoload :ResqueAdapter
46
+ autoload :SidekiqAdapter
47
+ autoload :SneakersAdapter
48
+ autoload :SuckerPunchAdapter
49
+ autoload :TestAdapter
50
+ end
51
+ end
@@ -2,21 +2,32 @@ require 'backburner'
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
+ # == Backburner adapter for Active Job
6
+ #
7
+ # Backburner is a beanstalkd-powered job queue that can handle a very
8
+ # high volume of jobs. You create background jobs and place them on
9
+ # multiple work queues to be processed later. Read more about
10
+ # Backburner {here}[https://github.com/nesquena/backburner].
11
+ #
12
+ # To use Backburner set the queue_adapter config to +:backburner+.
13
+ #
14
+ # Rails.application.config.active_job.queue_adapter = :backburner
5
15
  class BackburnerAdapter
6
16
  class << self
7
- def enqueue(job, *args)
8
- Backburner::Worker.enqueue JobWrapper, [ job.name, *args ], queue: job.queue_name
17
+ def enqueue(job) #:nodoc:
18
+ Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name
9
19
  end
10
20
 
11
- def enqueue_at(job, timestamp, *args)
12
- raise NotImplementedError
21
+ def enqueue_at(job, timestamp) #:nodoc:
22
+ delay = timestamp - Time.current.to_f
23
+ Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name, delay: delay
13
24
  end
14
25
  end
15
26
 
16
- class JobWrapper
27
+ class JobWrapper #:nodoc:
17
28
  class << self
18
- def perform(job_name, *args)
19
- job_name.constantize.new.execute(*args)
29
+ def perform(job_data)
30
+ Base.execute job_data
20
31
  end
21
32
  end
22
33
  end
@@ -2,20 +2,36 @@ require 'delayed_job'
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
+ # == Delayed Job adapter for Active Job
6
+ #
7
+ # Delayed::Job (or DJ) encapsulates the common pattern of asynchronously
8
+ # executing longer tasks in the background. Although DJ can have many
9
+ # storage backends one of the most used is based on Active Record.
10
+ # Read more about Delayed Job {here}[https://github.com/collectiveidea/delayed_job].
11
+ #
12
+ # To use Delayed Job set the queue_adapter config to +:delayed_job+.
13
+ #
14
+ # Rails.application.config.active_job.queue_adapter = :delayed_job
5
15
  class DelayedJobAdapter
6
16
  class << self
7
- def enqueue(job, *args)
8
- JobWrapper.new.delay(queue: job.queue_name).perform(job, *args)
17
+ def enqueue(job) #:nodoc:
18
+ Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name)
9
19
  end
10
20
 
11
- def enqueue_at(job, timestamp, *args)
12
- JobWrapper.new.delay(queue: job.queue_name, run_at: Time.at(timestamp)).perform(job, *args)
21
+ def enqueue_at(job, timestamp) #:nodoc:
22
+ Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, run_at: Time.at(timestamp))
13
23
  end
14
24
  end
15
25
 
16
- class JobWrapper
17
- def perform(job, *args)
18
- job.new.execute(*args)
26
+ class JobWrapper #:nodoc:
27
+ attr_accessor :job_data
28
+
29
+ def initialize(job_data)
30
+ @job_data = job_data
31
+ end
32
+
33
+ def perform
34
+ Base.execute(job_data)
19
35
  end
20
36
  end
21
37
  end
@@ -1,12 +1,20 @@
1
1
  module ActiveJob
2
2
  module QueueAdapters
3
+ # == Active Job Inline adapter
4
+ #
5
+ # When enqueueing jobs with the Inline adapter the job will be executed
6
+ # immediately.
7
+ #
8
+ # To use the Inline set the queue_adapter config to +:inline+.
9
+ #
10
+ # Rails.application.config.active_job.queue_adapter = :inline
3
11
  class InlineAdapter
4
12
  class << self
5
- def enqueue(job, *args)
6
- job.new.execute(*args)
13
+ def enqueue(job) #:nodoc:
14
+ Base.execute(job.serialize)
7
15
  end
8
16
 
9
- def enqueue_at(*)
17
+ def enqueue_at(*) #:nodoc:
10
18
  raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at https://github.com/rails/activejob")
11
19
  end
12
20
  end
@@ -2,27 +2,39 @@ require 'qu'
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
+ # == Qu adapter for Active Job
6
+ #
7
+ # Qu is a Ruby library for queuing and processing background jobs. It is
8
+ # heavily inspired by delayed_job and Resque. Qu was created to overcome
9
+ # some shortcomings in the existing queuing libraries that we experienced.
10
+ # The advantages of Qu are: Multiple backends (redis, mongo), jobs are
11
+ # requeued when worker is killed, resque-like API.
12
+ #
13
+ # Read more about Que {here}[https://github.com/bkeepers/qu].
14
+ #
15
+ # To use Qu set the queue_adapter config to +:qu+.
16
+ #
17
+ # Rails.application.config.active_job.queue_adapter = :qu
5
18
  class QuAdapter
6
19
  class << self
7
- def enqueue(job, *args)
8
- Qu::Payload.new(klass: JobWrapper, args: [job.name, *args]).tap do |payload|
20
+ def enqueue(job, *args) #:nodoc:
21
+ Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
9
22
  payload.instance_variable_set(:@queue, job.queue_name)
10
23
  end.push
11
24
  end
12
25
 
13
- def enqueue_at(job, timestamp, *args)
26
+ def enqueue_at(job, timestamp, *args) #:nodoc:
14
27
  raise NotImplementedError
15
28
  end
16
29
  end
17
30
 
18
- class JobWrapper < Qu::Job
19
- def initialize(job_name, *args)
20
- @job = job_name.constantize
21
- @args = args
31
+ class JobWrapper < Qu::Job #:nodoc:
32
+ def initialize(job_data)
33
+ @job_data = job_data
22
34
  end
23
35
 
24
36
  def perform
25
- @job.new.execute(*@args)
37
+ Base.execute @job_data
26
38
  end
27
39
  end
28
40
  end
@@ -2,20 +2,32 @@ require 'que'
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
+ # == Que adapter for Active Job
6
+ #
7
+ # Que is a high-performance alternative to DelayedJob or QueueClassic that
8
+ # improves the reliability of your application by protecting your jobs with
9
+ # the same ACID guarantees as the rest of your data. Que is a queue for
10
+ # Ruby and PostgreSQL that manages jobs using advisory locks.
11
+ #
12
+ # Read more about Que {here}[https://github.com/chanks/que].
13
+ #
14
+ # To use Que set the queue_adapter config to +:que+.
15
+ #
16
+ # Rails.application.config.active_job.queue_adapter = :que
5
17
  class QueAdapter
6
18
  class << self
7
- def enqueue(job, *args)
8
- JobWrapper.enqueue job.name, *args, queue: job.queue_name
19
+ def enqueue(job) #:nodoc:
20
+ JobWrapper.enqueue job.serialize, queue: job.queue_name
9
21
  end
10
22
 
11
- def enqueue_at(job, timestamp, *args)
12
- raise NotImplementedError
23
+ def enqueue_at(job, timestamp) #:nodoc:
24
+ JobWrapper.enqueue job.serialize, queue: job.queue_name, run_at: Time.at(timestamp)
13
25
  end
14
26
  end
15
27
 
16
- class JobWrapper < Que::Job
17
- def run(job_name, *args)
18
- job_name.constantize.new.execute(*args)
28
+ class JobWrapper < Que::Job #:nodoc:
29
+ def run(job_data)
30
+ Base.execute job_data
19
31
  end
20
32
  end
21
33
  end
@@ -2,21 +2,50 @@ require 'queue_classic'
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
+ # == Queue Classic adapter for Active Job
6
+ #
7
+ # queue_classic provides a simple interface to a PostgreSQL-backed message
8
+ # queue. queue_classic specializes in concurrent locking and minimizing
9
+ # database load while providing a simple, intuitive developer experience.
10
+ # queue_classic assumes that you are already using PostgreSQL in your
11
+ # production environment and that adding another dependency (e.g. redis,
12
+ # beanstalkd, 0mq) is undesirable.
13
+ #
14
+ # Read more about Queue Classic {here}[https://github.com/ryandotsmith/queue_classic].
15
+ #
16
+ # To use Queue Classic set the queue_adapter config to +:queue_classic+.
17
+ #
18
+ # Rails.application.config.active_job.queue_adapter = :queue_classic
5
19
  class QueueClassicAdapter
6
20
  class << self
7
- def enqueue(job, *args)
8
- QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.name, *args)
21
+ def enqueue(job) #:nodoc:
22
+ build_queue(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.serialize)
9
23
  end
10
24
 
11
- def enqueue_at(job, timestamp, *args)
12
- raise NotImplementedError
25
+ def enqueue_at(job, timestamp) #:nodoc:
26
+ queue = build_queue(job.queue_name)
27
+ unless queue.respond_to?(:enqueue_at)
28
+ raise NotImplementedError, 'To be able to schedule jobs with Queue Classic ' \
29
+ 'the QC::Queue needs to respond to `enqueue_at(timestamp, method, *args)`. '
30
+ 'You can implement this yourself or you can use the queue_classic-later gem.'
31
+ end
32
+ queue.enqueue_at(timestamp, "#{JobWrapper.name}.perform", job.serialize)
33
+ end
34
+
35
+ # Builds a <tt>QC::Queue</tt> object to schedule jobs on.
36
+ #
37
+ # If you have a custom <tt>QC::Queue</tt> subclass you'll need to suclass
38
+ # <tt>ActiveJob::QueueAdapters::QueueClassicAdapter</tt> and override the
39
+ # <tt>build_queue</tt> method.
40
+ def build_queue(queue_name)
41
+ QC::Queue.new(queue_name)
13
42
  end
14
43
  end
15
44
 
16
- class JobWrapper
45
+ class JobWrapper #:nodoc:
17
46
  class << self
18
- def perform(job_name, *args)
19
- job_name.constantize.new.execute(*args)
47
+ def perform(job_data)
48
+ Base.execute job_data
20
49
  end
21
50
  end
22
51
  end