activejob 4.2.11.3 → 5.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activejob might be problematic. Click here for more details.

Files changed (33) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +88 -54
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +5 -5
  5. data/lib/active_job.rb +2 -1
  6. data/lib/active_job/arguments.rb +38 -19
  7. data/lib/active_job/async_job.rb +77 -0
  8. data/lib/active_job/base.rb +3 -1
  9. data/lib/active_job/callbacks.rb +2 -2
  10. data/lib/active_job/core.rb +42 -5
  11. data/lib/active_job/enqueuing.rb +5 -0
  12. data/lib/active_job/gem_version.rb +4 -4
  13. data/lib/active_job/logging.rb +17 -3
  14. data/lib/active_job/queue_adapter.rb +44 -16
  15. data/lib/active_job/queue_adapters.rb +86 -0
  16. data/lib/active_job/queue_adapters/async_adapter.rb +23 -0
  17. data/lib/active_job/queue_adapters/backburner_adapter.rb +6 -8
  18. data/lib/active_job/queue_adapters/delayed_job_adapter.rb +9 -7
  19. data/lib/active_job/queue_adapters/inline_adapter.rb +5 -7
  20. data/lib/active_job/queue_adapters/qu_adapter.rb +11 -9
  21. data/lib/active_job/queue_adapters/que_adapter.rb +9 -7
  22. data/lib/active_job/queue_adapters/queue_classic_adapter.rb +22 -20
  23. data/lib/active_job/queue_adapters/resque_adapter.rb +8 -10
  24. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +15 -17
  25. data/lib/active_job/queue_adapters/sneakers_adapter.rb +11 -11
  26. data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +5 -7
  27. data/lib/active_job/queue_adapters/test_adapter.rb +25 -16
  28. data/lib/active_job/queue_name.rb +1 -1
  29. data/lib/active_job/queue_priority.rb +44 -0
  30. data/lib/active_job/test_helper.rb +144 -46
  31. data/lib/rails/generators/job/job_generator.rb +1 -2
  32. data/lib/rails/generators/job/templates/job.rb +1 -1
  33. metadata +15 -10
@@ -1,6 +1,7 @@
1
1
  require 'active_job/core'
2
2
  require 'active_job/queue_adapter'
3
3
  require 'active_job/queue_name'
4
+ require 'active_job/queue_priority'
4
5
  require 'active_job/enqueuing'
5
6
  require 'active_job/execution'
6
7
  require 'active_job/callbacks'
@@ -35,7 +36,7 @@ module ActiveJob #:nodoc:
35
36
  # Records that are passed in are serialized/deserialized using Global
36
37
  # ID. More information can be found in Arguments.
37
38
  #
38
- # To enqueue a job to be performed as soon the queueing system is free:
39
+ # To enqueue a job to be performed as soon as the queueing system is free:
39
40
  #
40
41
  # ProcessPhotoJob.perform_later(photo)
41
42
  #
@@ -57,6 +58,7 @@ module ActiveJob #:nodoc:
57
58
  include Core
58
59
  include QueueAdapter
59
60
  include QueueName
61
+ include QueuePriority
60
62
  include Enqueuing
61
63
  include Execution
62
64
  include Callbacks
@@ -3,8 +3,8 @@ require 'active_support/callbacks'
3
3
  module ActiveJob
4
4
  # = Active Job Callbacks
5
5
  #
6
- # Active Job provides hooks during the lifecycle of a job. Callbacks allow you
7
- # to trigger logic during the lifecycle of a job. Available callbacks are:
6
+ # Active Job provides hooks during the life cycle of a job. Callbacks allow you
7
+ # to trigger logic during the life cycle of a job. Available callbacks are:
8
8
  #
9
9
  # * <tt>before_enqueue</tt>
10
10
  # * <tt>around_enqueue</tt>
@@ -1,4 +1,6 @@
1
1
  module ActiveJob
2
+ # Provides general behavior that will be included into every Active Job
3
+ # object that inherits from ActiveJob::Base.
2
4
  module Core
3
5
  extend ActiveSupport::Concern
4
6
 
@@ -16,6 +18,12 @@ module ActiveJob
16
18
  # Queue in which the job will reside.
17
19
  attr_writer :queue_name
18
20
 
21
+ # Priority that the job will have (lower is more priority).
22
+ attr_writer :priority
23
+
24
+ # ID optionally provided by adapter
25
+ attr_accessor :provider_job_id
26
+
19
27
  # I18n.locale to be used during the job.
20
28
  attr_accessor :locale
21
29
  end
@@ -25,11 +33,8 @@ module ActiveJob
25
33
  module ClassMethods
26
34
  # Creates a new job instance from a hash created with +serialize+
27
35
  def deserialize(job_data)
28
- job = job_data['job_class'].constantize.new
29
- job.job_id = job_data['job_id']
30
- job.queue_name = job_data['queue_name']
31
- job.serialized_arguments = job_data['arguments']
32
- job.locale = job_data['locale'] || I18n.locale
36
+ job = job_data['job_class'].constantize.new
37
+ job.deserialize(job_data)
33
38
  job
34
39
  end
35
40
 
@@ -41,6 +46,7 @@ module ActiveJob
41
46
  # * <tt>:wait</tt> - Enqueues the job with the specified delay
42
47
  # * <tt>:wait_until</tt> - Enqueues the job at the time specified
43
48
  # * <tt>:queue</tt> - Enqueues the job on the specified queue
49
+ # * <tt>:priority</tt> - Enqueues the job with the specified priority
44
50
  #
45
51
  # ==== Examples
46
52
  #
@@ -49,6 +55,7 @@ module ActiveJob
49
55
  # VideoJob.set(wait_until: Time.now.tomorrow).perform_later(Video.last)
50
56
  # VideoJob.set(queue: :some_queue, wait: 5.minutes).perform_later(Video.last)
51
57
  # VideoJob.set(queue: :some_queue, wait_until: Time.now.tomorrow).perform_later(Video.last)
58
+ # VideoJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later(Video.last)
52
59
  def set(options={})
53
60
  ConfiguredJob.new(self, options)
54
61
  end
@@ -60,6 +67,7 @@ module ActiveJob
60
67
  @arguments = arguments
61
68
  @job_id = SecureRandom.uuid
62
69
  @queue_name = self.class.queue_name
70
+ @priority = self.class.priority
63
71
  end
64
72
 
65
73
  # Returns a hash with the job data that can safely be passed to the
@@ -69,11 +77,40 @@ module ActiveJob
69
77
  'job_class' => self.class.name,
70
78
  'job_id' => job_id,
71
79
  'queue_name' => queue_name,
80
+ 'priority' => priority,
72
81
  'arguments' => serialize_arguments(arguments),
73
82
  'locale' => I18n.locale
74
83
  }
75
84
  end
76
85
 
86
+ # Attaches the stored job data to the current instance. Receives a hash
87
+ # returned from +serialize+
88
+ #
89
+ # ==== Examples
90
+ #
91
+ # class DeliverWebhookJob < ActiveJob::Base
92
+ # def serialize
93
+ # super.merge('attempt_number' => (@attempt_number || 0) + 1)
94
+ # end
95
+ #
96
+ # def deserialize(job_data)
97
+ # super
98
+ # @attempt_number = job_data['attempt_number']
99
+ # end
100
+ #
101
+ # rescue_from(TimeoutError) do |exception|
102
+ # raise exception if @attempt_number > 5
103
+ # retry_job(wait: 10)
104
+ # end
105
+ # end
106
+ def deserialize(job_data)
107
+ self.job_id = job_data['job_id']
108
+ self.queue_name = job_data['queue_name']
109
+ self.priority = job_data['priority']
110
+ self.serialized_arguments = job_data['arguments']
111
+ self.locale = job_data['locale'] || I18n.locale
112
+ end
113
+
77
114
  private
78
115
  def deserialize_arguments_if_needed
79
116
  if defined?(@serialized_arguments) && @serialized_arguments.present?
@@ -1,6 +1,7 @@
1
1
  require 'active_job/arguments'
2
2
 
3
3
  module ActiveJob
4
+ # Provides behavior for enqueuing and retrying jobs.
4
5
  module Enqueuing
5
6
  extend ActiveSupport::Concern
6
7
 
@@ -31,6 +32,7 @@ module ActiveJob
31
32
  # * <tt>:wait</tt> - Enqueues the job with the specified delay
32
33
  # * <tt>:wait_until</tt> - Enqueues the job at the time specified
33
34
  # * <tt>:queue</tt> - Enqueues the job on the specified queue
35
+ # * <tt>:priority</tt> - Enqueues the job with the specified priority
34
36
  #
35
37
  # ==== Examples
36
38
  #
@@ -53,6 +55,7 @@ module ActiveJob
53
55
  # * <tt>:wait</tt> - Enqueues the job with the specified delay
54
56
  # * <tt>:wait_until</tt> - Enqueues the job at the time specified
55
57
  # * <tt>:queue</tt> - Enqueues the job on the specified queue
58
+ # * <tt>:priority</tt> - Enqueues the job with the specified priority
56
59
  #
57
60
  # ==== Examples
58
61
  #
@@ -60,10 +63,12 @@ module ActiveJob
60
63
  # my_job_instance.enqueue wait: 5.minutes
61
64
  # my_job_instance.enqueue queue: :important
62
65
  # my_job_instance.enqueue wait_until: Date.tomorrow.midnight
66
+ # my_job_instance.enqueue priority: 10
63
67
  def enqueue(options={})
64
68
  self.scheduled_at = options[:wait].seconds.from_now.to_f if options[:wait]
65
69
  self.scheduled_at = options[:wait_until].to_f if options[:wait_until]
66
70
  self.queue_name = self.class.queue_name_from_part(options[:queue]) if options[:queue]
71
+ self.priority = options[:priority].to_i if options[:priority]
67
72
  run_callbacks :enqueue do
68
73
  if self.scheduled_at
69
74
  self.class.queue_adapter.enqueue_at self, self.scheduled_at
@@ -5,10 +5,10 @@ module ActiveJob
5
5
  end
6
6
 
7
7
  module VERSION
8
- MAJOR = 4
9
- MINOR = 2
10
- TINY = 11
11
- PRE = "3"
8
+ MAJOR = 5
9
+ MINOR = 0
10
+ TINY = 0
11
+ PRE = "beta1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/hash/transform_values'
1
2
  require 'active_support/core_ext/string/filters'
2
3
  require 'active_support/tagged_logging'
3
4
  require 'active_support/logger'
@@ -40,7 +41,7 @@ module ActiveJob
40
41
  def tag_logger(*tags)
41
42
  if logger.respond_to?(:tagged)
42
43
  tags.unshift "ActiveJob" unless logger_tagged_by_active_job?
43
- logger.tagged(*tags){ yield }
44
+ ActiveJob::Base.logger.tagged(*tags){ yield }
44
45
  else
45
46
  yield
46
47
  end
@@ -81,18 +82,31 @@ module ActiveJob
81
82
 
82
83
  private
83
84
  def queue_name(event)
84
- event.payload[:adapter].name.demodulize.remove('Adapter') + "(#{event.payload[:job].queue_name})"
85
+ event.payload[:adapter].class.name.demodulize.remove('Adapter') + "(#{event.payload[:job].queue_name})"
85
86
  end
86
87
 
87
88
  def args_info(job)
88
89
  if job.arguments.any?
89
90
  ' with arguments: ' +
90
- job.arguments.map { |arg| arg.try(:to_global_id).try(:to_s) || arg.inspect }.join(', ')
91
+ job.arguments.map { |arg| format(arg).inspect }.join(', ')
91
92
  else
92
93
  ''
93
94
  end
94
95
  end
95
96
 
97
+ def format(arg)
98
+ case arg
99
+ when Hash
100
+ arg.transform_values { |value| format(value) }
101
+ when Array
102
+ arg.map { |value| format(value) }
103
+ when GlobalID::Identification
104
+ arg.to_global_id rescue arg
105
+ else
106
+ arg
107
+ end
108
+ end
109
+
96
110
  def scheduled_at(event)
97
111
  Time.at(event.payload[:job].scheduled_at).utc
98
112
  end
@@ -1,35 +1,63 @@
1
1
  require 'active_job/queue_adapters/inline_adapter'
2
+ require 'active_support/core_ext/class/attribute'
2
3
  require 'active_support/core_ext/string/inflections'
3
4
 
4
5
  module ActiveJob
5
- # The <tt>ActionJob::QueueAdapter</tt> module is used to load the
6
- # correct adapter. The default queue adapter is the :inline queue.
6
+ # The <tt>ActiveJob::QueueAdapter</tt> module is used to load the
7
+ # correct adapter. The default queue adapter is the +:inline+ queue.
7
8
  module QueueAdapter #:nodoc:
8
9
  extend ActiveSupport::Concern
9
10
 
11
+ included do
12
+ class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false
13
+ self.queue_adapter = :inline
14
+ end
15
+
10
16
  # Includes the setter method for changing the active queue adapter.
11
17
  module ClassMethods
12
- mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
18
+ # Returns the backend queue provider. The default queue adapter
19
+ # is the +:inline+ queue. See QueueAdapters for more information.
20
+ def queue_adapter
21
+ _queue_adapter
22
+ end
13
23
 
14
24
  # Specify the backend queue provider. The default queue adapter
15
- # is the :inline queue. See QueueAdapters for more
25
+ # is the +:inline+ queue. See QueueAdapters for more
16
26
  # information.
17
- def queue_adapter=(name_or_adapter)
18
- @@queue_adapter = \
19
- case name_or_adapter
20
- when :test
21
- ActiveJob::QueueAdapters::TestAdapter.new
22
- when Symbol, String
23
- load_adapter(name_or_adapter)
24
- else
25
- name_or_adapter if name_or_adapter.respond_to?(:enqueue)
26
- end
27
+ def queue_adapter=(name_or_adapter_or_class)
28
+ self._queue_adapter = interpret_adapter(name_or_adapter_or_class)
27
29
  end
28
30
 
29
31
  private
30
- def load_adapter(name)
31
- "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
32
+
33
+ def interpret_adapter(name_or_adapter_or_class)
34
+ case name_or_adapter_or_class
35
+ when Symbol, String
36
+ ActiveJob::QueueAdapters.lookup(name_or_adapter_or_class).new
37
+ else
38
+ if queue_adapter?(name_or_adapter_or_class)
39
+ name_or_adapter_or_class
40
+ elsif queue_adapter_class?(name_or_adapter_or_class)
41
+ ActiveSupport::Deprecation.warn "Passing an adapter class is deprecated " \
42
+ "and will be removed in Rails 5.1. Please pass an adapter name " \
43
+ "(.queue_adapter = :#{name_or_adapter_or_class.name.demodulize.remove('Adapter').underscore}) " \
44
+ "or an instance (.queue_adapter = #{name_or_adapter_or_class.name}.new) instead."
45
+ name_or_adapter_or_class.new
46
+ else
47
+ raise ArgumentError
48
+ end
32
49
  end
50
+ end
51
+
52
+ QUEUE_ADAPTER_METHODS = [:enqueue, :enqueue_at].freeze
53
+
54
+ def queue_adapter?(object)
55
+ QUEUE_ADAPTER_METHODS.all? { |meth| object.respond_to?(meth) }
56
+ end
57
+
58
+ def queue_adapter_class?(object)
59
+ object.is_a?(Class) && QUEUE_ADAPTER_METHODS.all? { |meth| object.public_method_defined?(meth) }
60
+ end
33
61
  end
34
62
  end
35
63
  end
@@ -12,6 +12,8 @@ module ActiveJob
12
12
  # * {Sidekiq}[http://sidekiq.org]
13
13
  # * {Sneakers}[https://github.com/jondot/sneakers]
14
14
  # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
15
+ # * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
16
+ # * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
15
17
  #
16
18
  # === Backends Features
17
19
  #
@@ -26,15 +28,86 @@ module ActiveJob
26
28
  # | Sidekiq | Yes | Yes | Yes | Queue | No | Job |
27
29
  # | Sneakers | Yes | Yes | No | Queue | Queue | No |
28
30
  # | Sucker Punch | Yes | Yes | No | No | No | No |
31
+ # | Active Job Async | Yes | Yes | Yes | No | No | No |
29
32
  # | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A |
30
33
  #
34
+ # ==== Async
35
+ #
36
+ # Yes: The Queue Adapter runs the jobs in a separate or forked process.
37
+ #
38
+ # No: The job is run in the same process.
39
+ #
40
+ # ==== Queues
41
+ #
42
+ # Yes: Jobs may set which queue they are run in with queue_as or by using the set
43
+ # method.
44
+ #
45
+ # ==== Delayed
46
+ #
47
+ # Yes: The adapter will run the job in the future through perform_later.
48
+ #
49
+ # (Gem): An additional gem is required to use perform_later with this adapter.
50
+ #
51
+ # No: The adapter will run jobs at the next opportunity and cannot use perform_later.
52
+ #
53
+ # N/A: The adapter does not support queueing.
54
+ #
31
55
  # NOTE:
32
56
  # queue_classic supports job scheduling since version 3.1.
33
57
  # For older versions you can use the queue_classic-later gem.
34
58
  #
59
+ # ==== Priorities
60
+ #
61
+ # The order in which jobs are processed can be configured differently depending
62
+ # on the adapter.
63
+ #
64
+ # Job: Any class inheriting from the adapter may set the priority on the job
65
+ # object relative to other jobs.
66
+ #
67
+ # Queue: The adapter can set the priority for job queues, when setting a queue
68
+ # with Active Job this will be respected.
69
+ #
70
+ # Yes: Allows the priority to be set on the job object, at the queue level or
71
+ # as default configuration option.
72
+ #
73
+ # No: Does not allow the priority of jobs to be configured.
74
+ #
75
+ # N/A: The adapter does not support queueing, and therefore sorting them.
76
+ #
77
+ # ==== Timeout
78
+ #
79
+ # When a job will stop after the allotted time.
80
+ #
81
+ # Job: The timeout can be set for each instance of the job class.
82
+ #
83
+ # Queue: The timeout is set for all jobs on the queue.
84
+ #
85
+ # Global: The adapter is configured that all jobs have a maximum run time.
86
+ #
87
+ # N/A: This adapter does not run in a separate process, and therefore timeout
88
+ # is unsupported.
89
+ #
90
+ # ==== Retries
91
+ #
92
+ # Job: The number of retries can be set per instance of the job class.
93
+ #
94
+ # Yes: The Number of retries can be configured globally, for each instance or
95
+ # on the queue. This adapter may also present failed instances of the job class
96
+ # that can be restarted.
97
+ #
98
+ # Global: The adapter has a global number of retries.
99
+ #
100
+ # N/A: The adapter does not run in a separate process, and therefore doesn't
101
+ # support retries.
102
+ #
103
+ # === Async and Inline Queue Adapters
104
+ #
105
+ # Active Job has two built-in queue adapters intended for development and
106
+ # testing: +:async+ and +:inline+.
35
107
  module QueueAdapters
36
108
  extend ActiveSupport::Autoload
37
109
 
110
+ autoload :AsyncAdapter
38
111
  autoload :InlineAdapter
39
112
  autoload :BackburnerAdapter
40
113
  autoload :DelayedJobAdapter
@@ -46,5 +119,18 @@ module ActiveJob
46
119
  autoload :SneakersAdapter
47
120
  autoload :SuckerPunchAdapter
48
121
  autoload :TestAdapter
122
+
123
+ ADAPTER = 'Adapter'.freeze
124
+ private_constant :ADAPTER
125
+
126
+ class << self
127
+ # Returns adapter for specified name.
128
+ #
129
+ # ActiveJob::QueueAdapters.lookup(:sidekiq)
130
+ # # => ActiveJob::QueueAdapters::SidekiqAdapter
131
+ def lookup(name)
132
+ const_get(name.to_s.camelize << ADAPTER)
133
+ end
134
+ end
49
135
  end
50
136
  end
@@ -0,0 +1,23 @@
1
+ require 'active_job/async_job'
2
+
3
+ module ActiveJob
4
+ module QueueAdapters
5
+ # == Active Job Async adapter
6
+ #
7
+ # When enqueueing jobs with the Async adapter the job will be executed
8
+ # asynchronously using {AsyncJob}[http://api.rubyonrails.org/classes/ActiveJob/AsyncJob.html].
9
+ #
10
+ # To use +AsyncJob+ set the queue_adapter config to +:async+.
11
+ #
12
+ # Rails.application.config.active_job.queue_adapter = :async
13
+ class AsyncAdapter
14
+ def enqueue(job) #:nodoc:
15
+ ActiveJob::AsyncJob.enqueue(job.serialize, queue: job.queue_name)
16
+ end
17
+
18
+ def enqueue_at(job, timestamp) #:nodoc:
19
+ ActiveJob::AsyncJob.enqueue_at(job.serialize, timestamp, queue: job.queue_name)
20
+ end
21
+ end
22
+ end
23
+ end