activejob 7.0.8 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -214
- data/MIT-LICENSE +1 -1
- data/README.md +2 -2
- data/lib/active_job/arguments.rb +18 -32
- data/lib/active_job/base.rb +1 -1
- data/lib/active_job/callbacks.rb +3 -8
- data/lib/active_job/configured_job.rb +4 -0
- data/lib/active_job/core.rb +11 -6
- data/lib/active_job/deprecator.rb +7 -0
- data/lib/active_job/enqueue_after_transaction_commit.rb +28 -0
- data/lib/active_job/enqueuing.rb +71 -12
- data/lib/active_job/exceptions.rb +44 -7
- data/lib/active_job/execution.rb +5 -2
- data/lib/active_job/gem_version.rb +3 -3
- data/lib/active_job/instrumentation.rb +18 -10
- data/lib/active_job/log_subscriber.rb +80 -8
- data/lib/active_job/logging.rb +16 -2
- data/lib/active_job/queue_adapter.rb +18 -6
- data/lib/active_job/queue_adapters/abstract_adapter.rb +27 -0
- data/lib/active_job/queue_adapters/async_adapter.rb +3 -3
- data/lib/active_job/queue_adapters/backburner_adapter.rb +8 -4
- data/lib/active_job/queue_adapters/delayed_job_adapter.rb +10 -2
- data/lib/active_job/queue_adapters/inline_adapter.rb +6 -2
- data/lib/active_job/queue_adapters/queue_classic_adapter.rb +13 -5
- data/lib/active_job/queue_adapters/resque_adapter.rb +2 -2
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +43 -15
- data/lib/active_job/queue_adapters/sneakers_adapter.rb +2 -2
- data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +4 -4
- data/lib/active_job/queue_adapters/test_adapter.rb +13 -5
- data/lib/active_job/queue_adapters.rb +9 -7
- data/lib/active_job/queue_priority.rb +18 -1
- data/lib/active_job/railtie.rb +38 -7
- data/lib/active_job/serializers/big_decimal_serializer.rb +22 -0
- data/lib/active_job/serializers/duration_serializer.rb +4 -2
- data/lib/active_job/serializers/object_serializer.rb +2 -0
- data/lib/active_job/serializers/time_with_zone_serializer.rb +11 -2
- data/lib/active_job/serializers.rb +7 -3
- data/lib/active_job/test_helper.rb +60 -19
- data/lib/active_job/version.rb +1 -1
- data/lib/active_job.rb +34 -4
- data/lib/rails/generators/job/USAGE +19 -0
- data/lib/rails/generators/job/job_generator.rb +6 -2
- data/lib/rails/generators/job/templates/job.rb.tt +1 -1
- metadata +14 -10
- data/lib/active_job/queue_adapters/que_adapter.rb +0 -61
@@ -9,6 +9,7 @@ module ActiveJob
|
|
9
9
|
|
10
10
|
included do
|
11
11
|
class_attribute :retry_jitter, instance_accessor: false, instance_predicate: false, default: 0.0
|
12
|
+
class_attribute :after_discard_procs, default: []
|
12
13
|
end
|
13
14
|
|
14
15
|
module ClassMethods
|
@@ -20,13 +21,16 @@ module ActiveJob
|
|
20
21
|
# You can also pass a block that'll be invoked if the retry attempts fail for custom logic rather than letting
|
21
22
|
# the exception bubble up. This block is yielded with the job instance as the first and the error instance as the second parameter.
|
22
23
|
#
|
24
|
+
# +retry_on+ and +discard_on+ handlers are searched from bottom to top, and up the class hierarchy. The handler of the first class for
|
25
|
+
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if any.
|
26
|
+
#
|
23
27
|
# ==== Options
|
24
28
|
# * <tt>:wait</tt> - Re-enqueues the job with a delay specified either in seconds (default: 3 seconds),
|
25
29
|
# as a computing proc that takes the number of executions so far as an argument, or as a symbol reference of
|
26
|
-
# <tt>:
|
30
|
+
# <tt>:polynomially_longer</tt>, which applies the wait algorithm of <tt>((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2</tt>
|
27
31
|
# (first wait ~3s, then ~18s, then ~83s, etc)
|
28
|
-
# * <tt>:attempts</tt> -
|
29
|
-
# to retry the job until it succeeds
|
32
|
+
# * <tt>:attempts</tt> - Enqueues the job the specified number of times (default: 5 attempts) or a symbol reference of <tt>:unlimited</tt>
|
33
|
+
# to retry the job until it succeeds. The number of attempts includes the original job execution.
|
30
34
|
# * <tt>:queue</tt> - Re-enqueues the job on a different queue
|
31
35
|
# * <tt>:priority</tt> - Re-enqueues the job with a different priority
|
32
36
|
# * <tt>:jitter</tt> - A random delay of wait time used when calculating backoff. The default is 15% (0.15) which represents the upper bound of possible wait time (expressed as a percentage)
|
@@ -39,11 +43,11 @@ module ActiveJob
|
|
39
43
|
# retry_on CustomInfrastructureException, wait: 5.minutes, attempts: :unlimited
|
40
44
|
#
|
41
45
|
# retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
|
42
|
-
# retry_on Net::OpenTimeout, Timeout::Error, wait: :
|
46
|
+
# retry_on Net::OpenTimeout, Timeout::Error, wait: :polynomially_longer, attempts: 10 # retries at most 10 times for Net::OpenTimeout and Timeout::Error combined
|
43
47
|
# # To retry at most 10 times for each individual exception:
|
44
|
-
# # retry_on Net::OpenTimeout, wait: :
|
48
|
+
# # retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 10
|
45
49
|
# # retry_on Net::ReadTimeout, wait: 5.seconds, jitter: 0.30, attempts: 10
|
46
|
-
# # retry_on Timeout::Error, wait: :
|
50
|
+
# # retry_on Timeout::Error, wait: :polynomially_longer, attempts: 10
|
47
51
|
#
|
48
52
|
# retry_on(YetAnotherCustomAppException) do |job, error|
|
49
53
|
# ExceptionNotifier.caught(error)
|
@@ -65,8 +69,10 @@ module ActiveJob
|
|
65
69
|
instrument :retry_stopped, error: error do
|
66
70
|
yield self, error
|
67
71
|
end
|
72
|
+
run_after_discard_procs(error)
|
68
73
|
else
|
69
74
|
instrument :retry_stopped, error: error
|
75
|
+
run_after_discard_procs(error)
|
70
76
|
raise error
|
71
77
|
end
|
72
78
|
end
|
@@ -78,6 +84,9 @@ module ActiveJob
|
|
78
84
|
#
|
79
85
|
# You can also pass a block that'll be invoked. This block is yielded with the job instance as the first and the error instance as the second parameter.
|
80
86
|
#
|
87
|
+
# +retry_on+ and +discard_on+ handlers are searched from bottom to top, and up the class hierarchy. The handler of the first class for
|
88
|
+
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if any.
|
89
|
+
#
|
81
90
|
# ==== Example
|
82
91
|
#
|
83
92
|
# class SearchIndexingJob < ActiveJob::Base
|
@@ -95,9 +104,26 @@ module ActiveJob
|
|
95
104
|
rescue_from(*exceptions) do |error|
|
96
105
|
instrument :discard, error: error do
|
97
106
|
yield self, error if block_given?
|
107
|
+
run_after_discard_procs(error)
|
98
108
|
end
|
99
109
|
end
|
100
110
|
end
|
111
|
+
|
112
|
+
# A block to run when a job is about to be discarded for any reason.
|
113
|
+
#
|
114
|
+
# ==== Example
|
115
|
+
#
|
116
|
+
# class WorkJob < ActiveJob::Base
|
117
|
+
# after_discard do |job, exception|
|
118
|
+
# ExceptionNotifier.report(exception)
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# ...
|
122
|
+
#
|
123
|
+
# end
|
124
|
+
def after_discard(&blk)
|
125
|
+
self.after_discard_procs += [blk]
|
126
|
+
end
|
101
127
|
end
|
102
128
|
|
103
129
|
# Reschedules the job to be re-executed. This is useful in combination with
|
@@ -136,7 +162,8 @@ module ActiveJob
|
|
136
162
|
jitter = jitter == JITTER_DEFAULT ? self.class.retry_jitter : (jitter || 0.0)
|
137
163
|
|
138
164
|
case seconds_or_duration_or_algorithm
|
139
|
-
when
|
165
|
+
when :polynomially_longer
|
166
|
+
# This delay uses a polynomial backoff strategy, which was previously misnamed as exponential
|
140
167
|
delay = executions**4
|
141
168
|
delay_jitter = determine_jitter_for_delay(delay, jitter)
|
142
169
|
delay + delay_jitter + 2
|
@@ -165,5 +192,15 @@ module ActiveJob
|
|
165
192
|
executions
|
166
193
|
end
|
167
194
|
end
|
195
|
+
|
196
|
+
def run_after_discard_procs(exception)
|
197
|
+
exceptions = []
|
198
|
+
after_discard_procs.each do |blk|
|
199
|
+
instance_exec(self, exception, &blk)
|
200
|
+
rescue StandardError => e
|
201
|
+
exceptions << e
|
202
|
+
end
|
203
|
+
raise exceptions.last unless exceptions.empty?
|
204
|
+
end
|
168
205
|
end
|
169
206
|
end
|
data/lib/active_job/execution.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/rescuable"
|
4
|
-
require "active_job/arguments"
|
5
4
|
|
6
5
|
module ActiveJob
|
7
6
|
# = Active Job \Execution
|
@@ -51,7 +50,11 @@ module ActiveJob
|
|
51
50
|
|
52
51
|
_perform_job
|
53
52
|
rescue Exception => exception
|
54
|
-
rescue_with_handler(exception)
|
53
|
+
handled = rescue_with_handler(exception)
|
54
|
+
return handled if handled
|
55
|
+
|
56
|
+
run_after_discard_procs(exception)
|
57
|
+
raise
|
55
58
|
end
|
56
59
|
|
57
60
|
def perform(*)
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveJob
|
4
|
-
# Returns the currently loaded version of Active Job as a
|
4
|
+
# Returns the currently loaded version of Active Job as a +Gem::Version+.
|
5
5
|
def self.gem_version
|
6
6
|
Gem::Version.new VERSION::STRING
|
7
7
|
end
|
8
8
|
|
9
9
|
module VERSION
|
10
10
|
MAJOR = 7
|
11
|
-
MINOR =
|
12
|
-
TINY =
|
11
|
+
MINOR = 2
|
12
|
+
TINY = 0
|
13
13
|
PRE = nil
|
14
14
|
|
15
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
@@ -1,6 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveJob
|
4
|
+
class << self
|
5
|
+
private
|
6
|
+
def instrument_enqueue_all(queue_adapter, jobs)
|
7
|
+
payload = { adapter: queue_adapter, jobs: jobs }
|
8
|
+
ActiveSupport::Notifications.instrument("enqueue_all.active_job", payload) do
|
9
|
+
result = yield payload
|
10
|
+
payload[:enqueued_count] = result
|
11
|
+
result
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
4
16
|
module Instrumentation # :nodoc:
|
5
17
|
extend ActiveSupport::Concern
|
6
18
|
|
@@ -21,19 +33,15 @@ module ActiveJob
|
|
21
33
|
end
|
22
34
|
|
23
35
|
def instrument(operation, payload = {}, &block)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
if defined?(@_halted_callback_hook_called) && @_halted_callback_hook_called
|
28
|
-
event_payload[:aborted] = true
|
29
|
-
@_halted_callback_hook_called = nil
|
30
|
-
end
|
36
|
+
payload[:job] = self
|
37
|
+
payload[:adapter] = queue_adapter
|
31
38
|
|
39
|
+
ActiveSupport::Notifications.instrument("#{operation}.active_job", payload) do
|
40
|
+
value = block.call if block
|
41
|
+
payload[:aborted] = @_halted_callback_hook_called if defined?(@_halted_callback_hook_called)
|
42
|
+
@_halted_callback_hook_called = nil
|
32
43
|
value
|
33
44
|
end
|
34
|
-
|
35
|
-
ActiveSupport::Notifications.instrument \
|
36
|
-
"#{operation}.active_job", payload.merge(adapter: queue_adapter, job: self), &enhanced_block
|
37
45
|
end
|
38
46
|
|
39
47
|
def halted_callback_hook(*)
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/string/filters"
|
4
3
|
require "active_support/log_subscriber"
|
5
4
|
|
6
5
|
module ActiveJob
|
7
6
|
class LogSubscriber < ActiveSupport::LogSubscriber # :nodoc:
|
7
|
+
class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
|
8
|
+
|
8
9
|
def enqueue(event)
|
9
10
|
job = event.payload[:job]
|
10
11
|
ex = event.payload[:exception_object] || job.enqueue_error
|
@@ -23,6 +24,7 @@ module ActiveJob
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
27
|
+
subscribe_log_level :enqueue, :info
|
26
28
|
|
27
29
|
def enqueue_at(event)
|
28
30
|
job = event.payload[:job]
|
@@ -38,17 +40,48 @@ module ActiveJob
|
|
38
40
|
end
|
39
41
|
else
|
40
42
|
info do
|
41
|
-
"Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)
|
43
|
+
"Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
subscribe_log_level :enqueue_at, :info
|
48
|
+
|
49
|
+
def enqueue_all(event)
|
50
|
+
info do
|
51
|
+
jobs = event.payload[:jobs]
|
52
|
+
adapter = event.payload[:adapter]
|
53
|
+
enqueued_count = event.payload[:enqueued_count]
|
54
|
+
|
55
|
+
if enqueued_count == jobs.size
|
56
|
+
enqueued_jobs_message(adapter, jobs)
|
57
|
+
elsif jobs.any?(&:successfully_enqueued?)
|
58
|
+
enqueued_jobs = jobs.select(&:successfully_enqueued?)
|
59
|
+
|
60
|
+
failed_enqueue_count = jobs.size - enqueued_count
|
61
|
+
if failed_enqueue_count == 0
|
62
|
+
enqueued_jobs_message(adapter, enqueued_jobs)
|
63
|
+
else
|
64
|
+
"#{enqueued_jobs_message(adapter, enqueued_jobs)}. "\
|
65
|
+
"Failed enqueuing #{failed_enqueue_count} #{'job'.pluralize(failed_enqueue_count)}"
|
66
|
+
end
|
67
|
+
else
|
68
|
+
failed_enqueue_count = jobs.size - enqueued_count
|
69
|
+
"Failed enqueuing #{failed_enqueue_count} #{'job'.pluralize(failed_enqueue_count)} "\
|
70
|
+
"to #{ActiveJob.adapter_name(adapter)}"
|
42
71
|
end
|
43
72
|
end
|
44
73
|
end
|
74
|
+
subscribe_log_level :enqueue_all, :info
|
45
75
|
|
46
76
|
def perform_start(event)
|
47
77
|
info do
|
48
78
|
job = event.payload[:job]
|
49
|
-
|
79
|
+
enqueue_info = job.enqueued_at.present? ? " enqueued at #{job.enqueued_at.utc.iso8601(9)}" : ""
|
80
|
+
|
81
|
+
"Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)}" + enqueue_info + args_info(job)
|
50
82
|
end
|
51
83
|
end
|
84
|
+
subscribe_log_level :perform_start, :info
|
52
85
|
|
53
86
|
def perform(event)
|
54
87
|
job = event.payload[:job]
|
@@ -67,6 +100,7 @@ module ActiveJob
|
|
67
100
|
end
|
68
101
|
end
|
69
102
|
end
|
103
|
+
subscribe_log_level :perform, :info
|
70
104
|
|
71
105
|
def enqueue_retry(event)
|
72
106
|
job = event.payload[:job]
|
@@ -75,34 +109,37 @@ module ActiveJob
|
|
75
109
|
|
76
110
|
info do
|
77
111
|
if ex
|
78
|
-
"Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
|
112
|
+
"Retrying #{job.class} (Job ID: #{job.job_id}) after #{job.executions} attempts in #{wait.to_i} seconds, due to a #{ex.class} (#{ex.message})."
|
79
113
|
else
|
80
|
-
"Retrying #{job.class} in #{wait.to_i} seconds."
|
114
|
+
"Retrying #{job.class} (Job ID: #{job.job_id}) after #{job.executions} attempts in #{wait.to_i} seconds."
|
81
115
|
end
|
82
116
|
end
|
83
117
|
end
|
118
|
+
subscribe_log_level :enqueue_retry, :info
|
84
119
|
|
85
120
|
def retry_stopped(event)
|
86
121
|
job = event.payload[:job]
|
87
122
|
ex = event.payload[:error]
|
88
123
|
|
89
124
|
error do
|
90
|
-
"Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
|
125
|
+
"Stopped retrying #{job.class} (Job ID: #{job.job_id}) due to a #{ex.class} (#{ex.message}), which reoccurred on #{job.executions} attempts."
|
91
126
|
end
|
92
127
|
end
|
128
|
+
subscribe_log_level :enqueue_retry, :error
|
93
129
|
|
94
130
|
def discard(event)
|
95
131
|
job = event.payload[:job]
|
96
132
|
ex = event.payload[:error]
|
97
133
|
|
98
134
|
error do
|
99
|
-
"Discarded #{job.class} due to a #{ex.class}."
|
135
|
+
"Discarded #{job.class} (Job ID: #{job.job_id}) due to a #{ex.class} (#{ex.message})."
|
100
136
|
end
|
101
137
|
end
|
138
|
+
subscribe_log_level :discard, :error
|
102
139
|
|
103
140
|
private
|
104
141
|
def queue_name(event)
|
105
|
-
event.payload[:adapter]
|
142
|
+
ActiveJob.adapter_name(event.payload[:adapter]) + "(#{event.payload[:job].queue_name})"
|
106
143
|
end
|
107
144
|
|
108
145
|
def args_info(job)
|
@@ -134,6 +171,41 @@ module ActiveJob
|
|
134
171
|
def logger
|
135
172
|
ActiveJob::Base.logger
|
136
173
|
end
|
174
|
+
|
175
|
+
def info(progname = nil, &block)
|
176
|
+
return unless super
|
177
|
+
|
178
|
+
if ActiveJob.verbose_enqueue_logs
|
179
|
+
log_enqueue_source
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def error(progname = nil, &block)
|
184
|
+
return unless super
|
185
|
+
|
186
|
+
if ActiveJob.verbose_enqueue_logs
|
187
|
+
log_enqueue_source
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def log_enqueue_source
|
192
|
+
source = extract_enqueue_source_location(caller)
|
193
|
+
|
194
|
+
if source
|
195
|
+
logger.info("↳ #{source}")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def extract_enqueue_source_location(locations)
|
200
|
+
backtrace_cleaner.clean(locations.lazy).first
|
201
|
+
end
|
202
|
+
|
203
|
+
def enqueued_jobs_message(adapter, enqueued_jobs)
|
204
|
+
enqueued_count = enqueued_jobs.size
|
205
|
+
job_classes_counts = enqueued_jobs.map(&:class).tally.sort_by { |_k, v| -v }
|
206
|
+
"Enqueued #{enqueued_count} #{'job'.pluralize(enqueued_count)} to #{ActiveJob.adapter_name(adapter)}"\
|
207
|
+
" (#{job_classes_counts.map { |klass, count| "#{count} #{klass}" }.join(', ')})"
|
208
|
+
end
|
137
209
|
end
|
138
210
|
end
|
139
211
|
|
data/lib/active_job/logging.rb
CHANGED
@@ -4,17 +4,31 @@ require "active_support/tagged_logging"
|
|
4
4
|
require "active_support/logger"
|
5
5
|
|
6
6
|
module ActiveJob
|
7
|
-
module Logging
|
7
|
+
module Logging
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
included do
|
11
|
+
##
|
12
|
+
# Accepts a logger conforming to the interface of Log4r or the default
|
13
|
+
# Ruby +Logger+ class. You can retrieve this logger by calling +logger+ on
|
14
|
+
# either an Active Job job class or an Active Job job instance.
|
11
15
|
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
16
|
+
|
17
|
+
##
|
18
|
+
# Configures whether a job's arguments should be logged. This can be
|
19
|
+
# useful when a job's arguments may be sensitive and so should not be
|
20
|
+
# logged.
|
21
|
+
#
|
22
|
+
# The value defaults to +true+, but this can be configured with
|
23
|
+
# +config.active_job.log_arguments+. Additionally, individual jobs can
|
24
|
+
# also configure a value, which will apply to themselves and any
|
25
|
+
# subclasses.
|
12
26
|
class_attribute :log_arguments, instance_accessor: false, default: true
|
13
27
|
|
14
28
|
around_enqueue(prepend: true) { |_, block| tag_logger(&block) }
|
15
29
|
end
|
16
30
|
|
17
|
-
def perform_now
|
31
|
+
def perform_now # :nodoc:
|
18
32
|
tag_logger(self.class.name, self.job_id) { super }
|
19
33
|
end
|
20
34
|
|
@@ -3,8 +3,20 @@
|
|
3
3
|
require "active_support/core_ext/string/inflections"
|
4
4
|
|
5
5
|
module ActiveJob
|
6
|
-
|
7
|
-
|
6
|
+
class << self
|
7
|
+
def adapter_name(adapter) # :nodoc:
|
8
|
+
return adapter.queue_adapter_name if adapter.respond_to?(:queue_adapter_name)
|
9
|
+
|
10
|
+
adapter_class = adapter.is_a?(Module) ? adapter : adapter.class
|
11
|
+
"#{adapter_class.name.demodulize.delete_suffix('Adapter')}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# = Active Job Queue adapter
|
16
|
+
#
|
17
|
+
# The +ActiveJob::QueueAdapter+ module is used to load the
|
18
|
+
# correct adapter. The default queue adapter is +:async+,
|
19
|
+
# which loads the ActiveJob::QueueAdapters::AsyncAdapter.
|
8
20
|
module QueueAdapter # :nodoc:
|
9
21
|
extend ActiveSupport::Concern
|
10
22
|
|
@@ -13,21 +25,21 @@ module ActiveJob
|
|
13
25
|
class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false
|
14
26
|
|
15
27
|
delegate :queue_adapter, to: :class
|
16
|
-
|
17
|
-
self.queue_adapter = :async
|
18
28
|
end
|
19
29
|
|
20
30
|
# Includes the setter method for changing the active queue adapter.
|
21
31
|
module ClassMethods
|
22
32
|
# Returns the backend queue provider. The default queue adapter
|
23
|
-
# is
|
33
|
+
# is +:async+. See QueueAdapters for more information.
|
24
34
|
def queue_adapter
|
35
|
+
self.queue_adapter = :async if _queue_adapter.nil?
|
25
36
|
_queue_adapter
|
26
37
|
end
|
27
38
|
|
28
39
|
# Returns string denoting the name of the configured queue adapter.
|
29
40
|
# By default returns <tt>"async"</tt>.
|
30
41
|
def queue_adapter_name
|
42
|
+
self.queue_adapter = :async if _queue_adapter_name.nil?
|
31
43
|
_queue_adapter_name
|
32
44
|
end
|
33
45
|
|
@@ -41,7 +53,7 @@ module ActiveJob
|
|
41
53
|
assign_adapter(name_or_adapter.to_s, queue_adapter)
|
42
54
|
else
|
43
55
|
if queue_adapter?(name_or_adapter)
|
44
|
-
adapter_name =
|
56
|
+
adapter_name = ActiveJob.adapter_name(name_or_adapter).underscore
|
45
57
|
assign_adapter(adapter_name, name_or_adapter)
|
46
58
|
else
|
47
59
|
raise ArgumentError
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module QueueAdapters
|
5
|
+
# = Active Job Abstract Adapter
|
6
|
+
#
|
7
|
+
# Active Job supports multiple job queue systems. ActiveJob::QueueAdapters::AbstractAdapter
|
8
|
+
# forms the abstraction layer which makes this possible.
|
9
|
+
class AbstractAdapter
|
10
|
+
# Defines whether enqueuing should happen implicitly to after commit when called
|
11
|
+
# from inside a transaction. Most adapters should return true, but some adapters
|
12
|
+
# that use the same database as Active Record and are transaction aware can return
|
13
|
+
# false to continue enqueuing jobs as part of the transaction.
|
14
|
+
def enqueue_after_transaction_commit?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def enqueue(job)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
def enqueue_at(job, timestamp)
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -7,7 +7,7 @@ require "concurrent/utility/processor_counter"
|
|
7
7
|
|
8
8
|
module ActiveJob
|
9
9
|
module QueueAdapters
|
10
|
-
#
|
10
|
+
# = Active Job Async adapter
|
11
11
|
#
|
12
12
|
# The Async adapter runs jobs with an in-process thread pool.
|
13
13
|
#
|
@@ -30,7 +30,7 @@ module ActiveJob
|
|
30
30
|
# The adapter uses a {Concurrent Ruby}[https://github.com/ruby-concurrency/concurrent-ruby] thread pool to schedule and execute
|
31
31
|
# jobs. Since jobs share a single thread pool, long-running jobs will block
|
32
32
|
# short-lived jobs. Fine for dev/test; bad for production.
|
33
|
-
class AsyncAdapter
|
33
|
+
class AsyncAdapter < AbstractAdapter
|
34
34
|
# See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html] for executor options.
|
35
35
|
def initialize(**executor_options)
|
36
36
|
@scheduler = Scheduler.new(**executor_options)
|
@@ -95,7 +95,7 @@ module ActiveJob
|
|
95
95
|
|
96
96
|
def enqueue_at(job, timestamp, queue_name:)
|
97
97
|
delay = timestamp - Time.current.to_f
|
98
|
-
if delay > 0
|
98
|
+
if !immediate && delay > 0
|
99
99
|
Concurrent::ScheduledTask.execute(delay, args: [job], executor: executor, &:perform)
|
100
100
|
else
|
101
101
|
enqueue(job, queue_name: queue_name)
|
@@ -4,7 +4,7 @@ require "backburner"
|
|
4
4
|
|
5
5
|
module ActiveJob
|
6
6
|
module QueueAdapters
|
7
|
-
#
|
7
|
+
# = Backburner adapter for Active Job
|
8
8
|
#
|
9
9
|
# Backburner is a beanstalkd-powered job queue that can handle a very
|
10
10
|
# high volume of jobs. You create background jobs and place them on
|
@@ -14,14 +14,18 @@ module ActiveJob
|
|
14
14
|
# To use Backburner set the queue_adapter config to +:backburner+.
|
15
15
|
#
|
16
16
|
# Rails.application.config.active_job.queue_adapter = :backburner
|
17
|
-
class BackburnerAdapter
|
17
|
+
class BackburnerAdapter < AbstractAdapter
|
18
18
|
def enqueue(job) # :nodoc:
|
19
|
-
Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
|
19
|
+
response = Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
|
20
|
+
job.provider_job_id = response[:id] if response.is_a?(Hash)
|
21
|
+
response
|
20
22
|
end
|
21
23
|
|
22
24
|
def enqueue_at(job, timestamp) # :nodoc:
|
23
25
|
delay = timestamp - Time.current.to_f
|
24
|
-
Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
|
26
|
+
response = Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
|
27
|
+
job.provider_job_id = response[:id] if response.is_a?(Hash)
|
28
|
+
response
|
25
29
|
end
|
26
30
|
|
27
31
|
class JobWrapper # :nodoc:
|
@@ -5,7 +5,7 @@ require "active_support/core_ext/string/inflections"
|
|
5
5
|
|
6
6
|
module ActiveJob
|
7
7
|
module QueueAdapters
|
8
|
-
#
|
8
|
+
# = Delayed Job adapter for Active Job
|
9
9
|
#
|
10
10
|
# Delayed::Job (or DJ) encapsulates the common pattern of asynchronously
|
11
11
|
# executing longer tasks in the background. Although DJ can have many
|
@@ -15,7 +15,15 @@ module ActiveJob
|
|
15
15
|
# To use Delayed Job, set the queue_adapter config to +:delayed_job+.
|
16
16
|
#
|
17
17
|
# Rails.application.config.active_job.queue_adapter = :delayed_job
|
18
|
-
class DelayedJobAdapter
|
18
|
+
class DelayedJobAdapter < AbstractAdapter
|
19
|
+
def initialize(enqueue_after_transaction_commit: false)
|
20
|
+
@enqueue_after_transaction_commit = enqueue_after_transaction_commit
|
21
|
+
end
|
22
|
+
|
23
|
+
def enqueue_after_transaction_commit? # :nodoc:
|
24
|
+
@enqueue_after_transaction_commit
|
25
|
+
end
|
26
|
+
|
19
27
|
def enqueue(job) # :nodoc:
|
20
28
|
delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, priority: job.priority)
|
21
29
|
job.provider_job_id = delayed_job.id
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveJob
|
4
4
|
module QueueAdapters
|
5
|
-
#
|
5
|
+
# = Active Job Inline adapter
|
6
6
|
#
|
7
7
|
# When enqueuing jobs with the Inline adapter the job will be executed
|
8
8
|
# immediately.
|
@@ -10,7 +10,11 @@ module ActiveJob
|
|
10
10
|
# To use the Inline set the queue_adapter config to +:inline+.
|
11
11
|
#
|
12
12
|
# Rails.application.config.active_job.queue_adapter = :inline
|
13
|
-
class InlineAdapter
|
13
|
+
class InlineAdapter < AbstractAdapter
|
14
|
+
def enqueue_after_transaction_commit? # :nodoc:
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
14
18
|
def enqueue(job) # :nodoc:
|
15
19
|
Base.execute(job.serialize)
|
16
20
|
end
|
@@ -4,7 +4,7 @@ require "queue_classic"
|
|
4
4
|
|
5
5
|
module ActiveJob
|
6
6
|
module QueueAdapters
|
7
|
-
#
|
7
|
+
# = queue_classic adapter for Active Job
|
8
8
|
#
|
9
9
|
# queue_classic provides a simple interface to a PostgreSQL-backed message
|
10
10
|
# queue. queue_classic specializes in concurrent locking and minimizing
|
@@ -18,7 +18,15 @@ module ActiveJob
|
|
18
18
|
# To use queue_classic set the queue_adapter config to +:queue_classic+.
|
19
19
|
#
|
20
20
|
# Rails.application.config.active_job.queue_adapter = :queue_classic
|
21
|
-
class QueueClassicAdapter
|
21
|
+
class QueueClassicAdapter < AbstractAdapter
|
22
|
+
def initialize(enqueue_after_transaction_commit: false)
|
23
|
+
@enqueue_after_transaction_commit = enqueue_after_transaction_commit
|
24
|
+
end
|
25
|
+
|
26
|
+
def enqueue_after_transaction_commit? # :nodoc:
|
27
|
+
@enqueue_after_transaction_commit
|
28
|
+
end
|
29
|
+
|
22
30
|
def enqueue(job) # :nodoc:
|
23
31
|
qc_job = build_queue(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.serialize)
|
24
32
|
job.provider_job_id = qc_job["id"] if qc_job.is_a?(Hash)
|
@@ -37,10 +45,10 @@ module ActiveJob
|
|
37
45
|
qc_job
|
38
46
|
end
|
39
47
|
|
40
|
-
# Builds a
|
48
|
+
# Builds a +QC::Queue+ object to schedule jobs on.
|
41
49
|
#
|
42
|
-
# If you have a custom
|
43
|
-
#
|
50
|
+
# If you have a custom +QC::Queue+ subclass you'll need to subclass
|
51
|
+
# +ActiveJob::QueueAdapters::QueueClassicAdapter+ and override the
|
44
52
|
# <tt>build_queue</tt> method.
|
45
53
|
def build_queue(queue_name)
|
46
54
|
QC::Queue.new(queue_name)
|
@@ -16,7 +16,7 @@ end
|
|
16
16
|
|
17
17
|
module ActiveJob
|
18
18
|
module QueueAdapters
|
19
|
-
#
|
19
|
+
# = Resque adapter for Active Job
|
20
20
|
#
|
21
21
|
# Resque (pronounced like "rescue") is a Redis-backed library for creating
|
22
22
|
# background jobs, placing those jobs on multiple queues, and processing
|
@@ -27,7 +27,7 @@ module ActiveJob
|
|
27
27
|
# To use Resque set the queue_adapter config to +:resque+.
|
28
28
|
#
|
29
29
|
# Rails.application.config.active_job.queue_adapter = :resque
|
30
|
-
class ResqueAdapter
|
30
|
+
class ResqueAdapter < AbstractAdapter
|
31
31
|
def enqueue(job) # :nodoc:
|
32
32
|
JobWrapper.instance_variable_set(:@queue, job.queue_name)
|
33
33
|
Resque.enqueue_to job.queue_name, JobWrapper, job.serialize
|