activejob 6.0.6.1 → 6.1.7.6
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 +108 -140
- data/MIT-LICENSE +1 -2
- data/README.md +1 -3
- data/lib/active_job/base.rb +3 -0
- data/lib/active_job/callbacks.rb +44 -4
- data/lib/active_job/core.rb +2 -2
- data/lib/active_job/enqueuing.rb +3 -13
- data/lib/active_job/exceptions.rb +29 -20
- data/lib/active_job/execution.rb +9 -1
- data/lib/active_job/gem_version.rb +3 -3
- data/lib/active_job/instrumentation.rb +40 -0
- data/lib/active_job/log_subscriber.rb +140 -0
- data/lib/active_job/logging.rb +3 -132
- data/lib/active_job/queue_adapter.rb +4 -1
- data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +1 -1
- data/lib/active_job/queue_adapters/test_adapter.rb +6 -2
- data/lib/active_job/queue_adapters.rb +5 -1
- data/lib/active_job/queue_name.rb +2 -2
- data/lib/active_job/railtie.rb +4 -0
- data/lib/active_job/serializers/date_time_serializer.rb +1 -5
- data/lib/active_job/serializers/module_serializer.rb +20 -0
- data/lib/active_job/serializers/object_serializer.rb +1 -1
- data/lib/active_job/serializers/time_object_serializer.rb +13 -0
- data/lib/active_job/serializers/time_serializer.rb +1 -5
- data/lib/active_job/serializers/time_with_zone_serializer.rb +1 -5
- data/lib/active_job/serializers.rb +4 -1
- data/lib/active_job/test_helper.rb +82 -80
- data/lib/active_job.rb +1 -1
- metadata +12 -8
@@ -7,6 +7,10 @@ module ActiveJob
|
|
7
7
|
module Exceptions
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
+
included do
|
11
|
+
class_attribute :retry_jitter, instance_accessor: false, instance_predicate: false, default: 0.0
|
12
|
+
end
|
13
|
+
|
10
14
|
module ClassMethods
|
11
15
|
# Catch the exception and reschedule job for re-execution after so many seconds, for a specific number of attempts.
|
12
16
|
# If the exception keeps getting raised beyond the specified number of attempts, the exception is allowed to
|
@@ -18,23 +22,25 @@ module ActiveJob
|
|
18
22
|
#
|
19
23
|
# ==== Options
|
20
24
|
# * <tt>:wait</tt> - Re-enqueues the job with a delay specified either in seconds (default: 3 seconds),
|
21
|
-
# as a computing proc that the number of executions so far as an argument, or as a symbol reference of
|
22
|
-
# <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>(executions **
|
23
|
-
# (first wait 3s, then 18s, then 83s, etc)
|
25
|
+
# as a computing proc that takes the number of executions so far as an argument, or as a symbol reference of
|
26
|
+
# <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2</tt>
|
27
|
+
# (first wait ~3s, then ~18s, then ~83s, etc)
|
24
28
|
# * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts)
|
25
29
|
# * <tt>:queue</tt> - Re-enqueues the job on a different queue
|
26
30
|
# * <tt>:priority</tt> - Re-enqueues the job with a different priority
|
31
|
+
# * <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)
|
27
32
|
#
|
28
33
|
# ==== Examples
|
29
34
|
#
|
30
35
|
# class RemoteServiceJob < ActiveJob::Base
|
31
|
-
# retry_on CustomAppException # defaults to 3s wait, 5 attempts
|
36
|
+
# retry_on CustomAppException # defaults to ~3s wait, 5 attempts
|
32
37
|
# retry_on AnotherCustomAppException, wait: ->(executions) { executions * 2 }
|
33
38
|
#
|
34
39
|
# retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
|
35
40
|
# retry_on Net::OpenTimeout, Timeout::Error, wait: :exponentially_longer, attempts: 10 # retries at most 10 times for Net::OpenTimeout and Timeout::Error combined
|
36
41
|
# # To retry at most 10 times for each individual exception:
|
37
42
|
# # retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
|
43
|
+
# # retry_on Net::ReadTimeout, wait: 5.seconds, jitter: 0.30, attempts: 10
|
38
44
|
# # retry_on Timeout::Error, wait: :exponentially_longer, attempts: 10
|
39
45
|
#
|
40
46
|
# retry_on(YetAnotherCustomAppException) do |job, error|
|
@@ -47,12 +53,11 @@ module ActiveJob
|
|
47
53
|
# # Might raise Net::OpenTimeout or Timeout::Error when the remote service is down
|
48
54
|
# end
|
49
55
|
# end
|
50
|
-
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
|
56
|
+
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil, jitter: JITTER_DEFAULT)
|
51
57
|
rescue_from(*exceptions) do |error|
|
52
58
|
executions = executions_for(exceptions)
|
53
|
-
|
54
59
|
if executions < attempts
|
55
|
-
retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions), queue: queue, priority: priority, error: error
|
60
|
+
retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions, jitter: jitter), queue: queue, priority: priority, error: error
|
56
61
|
else
|
57
62
|
if block_given?
|
58
63
|
instrument :retry_stopped, error: error do
|
@@ -115,22 +120,27 @@ module ActiveJob
|
|
115
120
|
# end
|
116
121
|
# end
|
117
122
|
def retry_job(options = {})
|
118
|
-
instrument :enqueue_retry,
|
123
|
+
instrument :enqueue_retry, options.slice(:error, :wait) do
|
119
124
|
enqueue options
|
120
125
|
end
|
121
126
|
end
|
122
127
|
|
123
128
|
private
|
124
|
-
|
129
|
+
JITTER_DEFAULT = Object.new
|
130
|
+
private_constant :JITTER_DEFAULT
|
131
|
+
|
132
|
+
def determine_delay(seconds_or_duration_or_algorithm:, executions:, jitter: JITTER_DEFAULT)
|
133
|
+
jitter = jitter == JITTER_DEFAULT ? self.class.retry_jitter : (jitter || 0.0)
|
134
|
+
|
125
135
|
case seconds_or_duration_or_algorithm
|
126
136
|
when :exponentially_longer
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
137
|
+
delay = executions**4
|
138
|
+
delay_jitter = determine_jitter_for_delay(delay, jitter)
|
139
|
+
delay + delay_jitter + 2
|
140
|
+
when ActiveSupport::Duration, Integer
|
141
|
+
delay = seconds_or_duration_or_algorithm.to_i
|
142
|
+
delay_jitter = determine_jitter_for_delay(delay, jitter)
|
143
|
+
delay + delay_jitter
|
134
144
|
when Proc
|
135
145
|
algorithm = seconds_or_duration_or_algorithm
|
136
146
|
algorithm.call(executions)
|
@@ -139,10 +149,9 @@ module ActiveJob
|
|
139
149
|
end
|
140
150
|
end
|
141
151
|
|
142
|
-
def
|
143
|
-
|
144
|
-
|
145
|
-
ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
|
152
|
+
def determine_jitter_for_delay(delay, jitter)
|
153
|
+
return 0.0 if jitter.zero?
|
154
|
+
Kernel.rand * delay * jitter
|
146
155
|
end
|
147
156
|
|
148
157
|
def executions_for(exceptions)
|
data/lib/active_job/execution.rb
CHANGED
@@ -29,13 +29,21 @@ module ActiveJob
|
|
29
29
|
|
30
30
|
# Performs the job immediately. The job is not sent to the queuing adapter
|
31
31
|
# but directly executed by blocking the execution of others until it's finished.
|
32
|
+
# +perform_now+ returns the value of your job's +perform+ method.
|
32
33
|
#
|
33
|
-
# MyJob
|
34
|
+
# class MyJob < ActiveJob::Base
|
35
|
+
# def perform
|
36
|
+
# "Hello World!"
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# puts MyJob.new(*args).perform_now # => "Hello World!"
|
34
41
|
def perform_now
|
35
42
|
# Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
|
36
43
|
self.executions = (executions || 0) + 1
|
37
44
|
|
38
45
|
deserialize_arguments_if_needed
|
46
|
+
|
39
47
|
run_callbacks :perform do
|
40
48
|
perform(*arguments)
|
41
49
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Instrumentation #:nodoc:
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
around_enqueue do |_, block|
|
9
|
+
scheduled_at ? instrument(:enqueue_at, &block) : instrument(:enqueue, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
around_perform do |_, block|
|
13
|
+
instrument :perform_start
|
14
|
+
instrument :perform, &block
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def instrument(operation, payload = {}, &block)
|
20
|
+
enhanced_block = ->(event_payload) do
|
21
|
+
value = block.call if block
|
22
|
+
|
23
|
+
if defined?(@_halted_callback_hook_called) && @_halted_callback_hook_called
|
24
|
+
event_payload[:aborted] = true
|
25
|
+
@_halted_callback_hook_called = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
ActiveSupport::Notifications.instrument \
|
32
|
+
"#{operation}.active_job", payload.merge(adapter: queue_adapter, job: self), &enhanced_block
|
33
|
+
end
|
34
|
+
|
35
|
+
def halted_callback_hook(*)
|
36
|
+
super
|
37
|
+
@_halted_callback_hook_called = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
4
|
+
require "active_support/log_subscriber"
|
5
|
+
|
6
|
+
module ActiveJob
|
7
|
+
class LogSubscriber < ActiveSupport::LogSubscriber #:nodoc:
|
8
|
+
def enqueue(event)
|
9
|
+
job = event.payload[:job]
|
10
|
+
ex = event.payload[:exception_object]
|
11
|
+
|
12
|
+
if ex
|
13
|
+
error do
|
14
|
+
"Failed enqueuing #{job.class.name} to #{queue_name(event)}: #{ex.class} (#{ex.message})"
|
15
|
+
end
|
16
|
+
elsif event.payload[:aborted]
|
17
|
+
info do
|
18
|
+
"Failed enqueuing #{job.class.name} to #{queue_name(event)}, a before_enqueue callback halted the enqueuing execution."
|
19
|
+
end
|
20
|
+
else
|
21
|
+
info do
|
22
|
+
"Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)}" + args_info(job)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def enqueue_at(event)
|
28
|
+
job = event.payload[:job]
|
29
|
+
ex = event.payload[:exception_object]
|
30
|
+
|
31
|
+
if ex
|
32
|
+
error do
|
33
|
+
"Failed enqueuing #{job.class.name} to #{queue_name(event)}: #{ex.class} (#{ex.message})"
|
34
|
+
end
|
35
|
+
elsif event.payload[:aborted]
|
36
|
+
info do
|
37
|
+
"Failed enqueuing #{job.class.name} to #{queue_name(event)}, a before_enqueue callback halted the enqueuing execution."
|
38
|
+
end
|
39
|
+
else
|
40
|
+
info do
|
41
|
+
"Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def perform_start(event)
|
47
|
+
info do
|
48
|
+
job = event.payload[:job]
|
49
|
+
"Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} enqueued at #{job.enqueued_at}" + args_info(job)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def perform(event)
|
54
|
+
job = event.payload[:job]
|
55
|
+
ex = event.payload[:exception_object]
|
56
|
+
if ex
|
57
|
+
error do
|
58
|
+
"Error performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms: #{ex.class} (#{ex.message}):\n" + Array(ex.backtrace).join("\n")
|
59
|
+
end
|
60
|
+
elsif event.payload[:aborted]
|
61
|
+
error do
|
62
|
+
"Error performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms: a before_perform callback halted the job execution"
|
63
|
+
end
|
64
|
+
else
|
65
|
+
info do
|
66
|
+
"Performed #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def enqueue_retry(event)
|
72
|
+
job = event.payload[:job]
|
73
|
+
ex = event.payload[:error]
|
74
|
+
wait = event.payload[:wait]
|
75
|
+
|
76
|
+
info do
|
77
|
+
if ex
|
78
|
+
"Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
|
79
|
+
else
|
80
|
+
"Retrying #{job.class} in #{wait.to_i} seconds."
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def retry_stopped(event)
|
86
|
+
job = event.payload[:job]
|
87
|
+
ex = event.payload[:error]
|
88
|
+
|
89
|
+
error do
|
90
|
+
"Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def discard(event)
|
95
|
+
job = event.payload[:job]
|
96
|
+
ex = event.payload[:error]
|
97
|
+
|
98
|
+
error do
|
99
|
+
"Discarded #{job.class} due to a #{ex.class}."
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
def queue_name(event)
|
105
|
+
event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
|
106
|
+
end
|
107
|
+
|
108
|
+
def args_info(job)
|
109
|
+
if job.class.log_arguments? && job.arguments.any?
|
110
|
+
" with arguments: " +
|
111
|
+
job.arguments.map { |arg| format(arg).inspect }.join(", ")
|
112
|
+
else
|
113
|
+
""
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def format(arg)
|
118
|
+
case arg
|
119
|
+
when Hash
|
120
|
+
arg.transform_values { |value| format(value) }
|
121
|
+
when Array
|
122
|
+
arg.map { |value| format(value) }
|
123
|
+
when GlobalID::Identification
|
124
|
+
arg.to_global_id rescue arg
|
125
|
+
else
|
126
|
+
arg
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def scheduled_at(event)
|
131
|
+
Time.at(event.payload[:job].scheduled_at).utc
|
132
|
+
end
|
133
|
+
|
134
|
+
def logger
|
135
|
+
ActiveJob::Base.logger
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
ActiveJob::LogSubscriber.attach_to :active_job
|
data/lib/active_job/logging.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/string/filters"
|
4
3
|
require "active_support/tagged_logging"
|
5
4
|
require "active_support/logger"
|
6
5
|
|
@@ -10,32 +9,10 @@ module ActiveJob
|
|
10
9
|
|
11
10
|
included do
|
12
11
|
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
12
|
+
class_attribute :log_arguments, instance_accessor: false, default: true
|
13
13
|
|
14
|
-
around_enqueue
|
15
|
-
|
16
|
-
block.call
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
around_perform do |job, block|
|
21
|
-
tag_logger(job.class.name, job.job_id) do
|
22
|
-
payload = { adapter: job.class.queue_adapter, job: job }
|
23
|
-
ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup)
|
24
|
-
ActiveSupport::Notifications.instrument("perform.active_job", payload) do
|
25
|
-
block.call
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
around_enqueue do |job, block|
|
31
|
-
if job.scheduled_at
|
32
|
-
ActiveSupport::Notifications.instrument("enqueue_at.active_job",
|
33
|
-
adapter: job.class.queue_adapter, job: job, &block)
|
34
|
-
else
|
35
|
-
ActiveSupport::Notifications.instrument("enqueue.active_job",
|
36
|
-
adapter: job.class.queue_adapter, job: job, &block)
|
37
|
-
end
|
38
|
-
end
|
14
|
+
around_enqueue { |_, block| tag_logger(&block) }
|
15
|
+
around_perform { |job, block| tag_logger(job.class.name, job.job_id, &block) }
|
39
16
|
end
|
40
17
|
|
41
18
|
private
|
@@ -51,111 +28,5 @@ module ActiveJob
|
|
51
28
|
def logger_tagged_by_active_job?
|
52
29
|
logger.formatter.current_tags.include?("ActiveJob")
|
53
30
|
end
|
54
|
-
|
55
|
-
class LogSubscriber < ActiveSupport::LogSubscriber #:nodoc:
|
56
|
-
def enqueue(event)
|
57
|
-
info do
|
58
|
-
job = event.payload[:job]
|
59
|
-
"Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)}" + args_info(job)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def enqueue_at(event)
|
64
|
-
info do
|
65
|
-
job = event.payload[:job]
|
66
|
-
"Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def perform_start(event)
|
71
|
-
info do
|
72
|
-
job = event.payload[:job]
|
73
|
-
"Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} enqueued at #{job.enqueued_at}" + args_info(job)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def perform(event)
|
78
|
-
job = event.payload[:job]
|
79
|
-
ex = event.payload[:exception_object]
|
80
|
-
if ex
|
81
|
-
error do
|
82
|
-
"Error performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms: #{ex.class} (#{ex.message}):\n" + Array(ex.backtrace).join("\n")
|
83
|
-
end
|
84
|
-
else
|
85
|
-
info do
|
86
|
-
"Performed #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def enqueue_retry(event)
|
92
|
-
job = event.payload[:job]
|
93
|
-
ex = event.payload[:error]
|
94
|
-
wait = event.payload[:wait]
|
95
|
-
|
96
|
-
info do
|
97
|
-
if ex
|
98
|
-
"Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
|
99
|
-
else
|
100
|
-
"Retrying #{job.class} in #{wait.to_i} seconds."
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def retry_stopped(event)
|
106
|
-
job = event.payload[:job]
|
107
|
-
ex = event.payload[:error]
|
108
|
-
|
109
|
-
error do
|
110
|
-
"Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def discard(event)
|
115
|
-
job = event.payload[:job]
|
116
|
-
ex = event.payload[:error]
|
117
|
-
|
118
|
-
error do
|
119
|
-
"Discarded #{job.class} due to a #{ex.class}."
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
private
|
124
|
-
def queue_name(event)
|
125
|
-
event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
|
126
|
-
end
|
127
|
-
|
128
|
-
def args_info(job)
|
129
|
-
if job.arguments.any?
|
130
|
-
" with arguments: " +
|
131
|
-
job.arguments.map { |arg| format(arg).inspect }.join(", ")
|
132
|
-
else
|
133
|
-
""
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def format(arg)
|
138
|
-
case arg
|
139
|
-
when Hash
|
140
|
-
arg.transform_values { |value| format(value) }
|
141
|
-
when Array
|
142
|
-
arg.map { |value| format(value) }
|
143
|
-
when GlobalID::Identification
|
144
|
-
arg.to_global_id rescue arg
|
145
|
-
else
|
146
|
-
arg
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def scheduled_at(event)
|
151
|
-
Time.at(event.payload[:job].scheduled_at).utc
|
152
|
-
end
|
153
|
-
|
154
|
-
def logger
|
155
|
-
ActiveJob::Base.logger
|
156
|
-
end
|
157
|
-
end
|
158
31
|
end
|
159
32
|
end
|
160
|
-
|
161
|
-
ActiveJob::Logging::LogSubscriber.attach_to :active_job
|
@@ -11,6 +11,9 @@ module ActiveJob
|
|
11
11
|
included do
|
12
12
|
class_attribute :_queue_adapter_name, instance_accessor: false, instance_predicate: false
|
13
13
|
class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false
|
14
|
+
|
15
|
+
delegate :queue_adapter, to: :class
|
16
|
+
|
14
17
|
self.queue_adapter = :async
|
15
18
|
end
|
16
19
|
|
@@ -23,7 +26,7 @@ module ActiveJob
|
|
23
26
|
end
|
24
27
|
|
25
28
|
# Returns string denoting the name of the configured queue adapter.
|
26
|
-
# By default returns
|
29
|
+
# By default returns <tt>"async"</tt>.
|
27
30
|
def queue_adapter_name
|
28
31
|
_queue_adapter_name
|
29
32
|
end
|
@@ -10,7 +10,7 @@ module ActiveJob
|
|
10
10
|
# This reduces the cost of hosting on a service like Heroku along
|
11
11
|
# with the memory footprint of having to maintain additional jobs if
|
12
12
|
# hosting on a dedicated server. All queues can run within a
|
13
|
-
# single application (
|
13
|
+
# single application (e.g. Rails, Sinatra, etc.) process.
|
14
14
|
#
|
15
15
|
# Read more about Sucker Punch {here}[https://github.com/brandonhilkert/sucker_punch].
|
16
16
|
#
|
@@ -12,7 +12,7 @@ module ActiveJob
|
|
12
12
|
#
|
13
13
|
# Rails.application.config.active_job.queue_adapter = :test
|
14
14
|
class TestAdapter
|
15
|
-
attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue)
|
15
|
+
attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at)
|
16
16
|
attr_writer(:enqueued_jobs, :performed_jobs)
|
17
17
|
|
18
18
|
# Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
|
@@ -54,7 +54,11 @@ module ActiveJob
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def filtered?(job)
|
57
|
-
filtered_queue?(job) || filtered_job_class?(job)
|
57
|
+
filtered_queue?(job) || filtered_job_class?(job) || filtered_time?(job)
|
58
|
+
end
|
59
|
+
|
60
|
+
def filtered_time?(job)
|
61
|
+
job.scheduled_at > at.to_f if at && job.scheduled_at
|
58
62
|
end
|
59
63
|
|
60
64
|
def filtered_queue?(job)
|
@@ -72,7 +72,7 @@ module ActiveJob
|
|
72
72
|
# Yes: Allows the priority to be set on the job object, at the queue level or
|
73
73
|
# as default configuration option.
|
74
74
|
#
|
75
|
-
# No:
|
75
|
+
# No: The adapter does not allow the priority of jobs to be configured.
|
76
76
|
#
|
77
77
|
# N/A: The adapter does not support queuing, and therefore sorting them.
|
78
78
|
#
|
@@ -86,6 +86,8 @@ module ActiveJob
|
|
86
86
|
#
|
87
87
|
# Global: The adapter is configured that all jobs have a maximum run time.
|
88
88
|
#
|
89
|
+
# No: The adapter does not allow the timeout of jobs to be configured.
|
90
|
+
#
|
89
91
|
# N/A: This adapter does not run in a separate process, and therefore timeout
|
90
92
|
# is unsupported.
|
91
93
|
#
|
@@ -99,6 +101,8 @@ module ActiveJob
|
|
99
101
|
#
|
100
102
|
# Global: The adapter has a global number of retries.
|
101
103
|
#
|
104
|
+
# No: The adapter does not allow the number of retries to be configured.
|
105
|
+
#
|
102
106
|
# N/A: The adapter does not run in a separate process, and therefore doesn't
|
103
107
|
# support retries.
|
104
108
|
#
|
@@ -6,7 +6,6 @@ module ActiveJob
|
|
6
6
|
|
7
7
|
# Includes the ability to override the default queue name and prefix.
|
8
8
|
module ClassMethods
|
9
|
-
mattr_accessor :queue_name_prefix
|
10
9
|
mattr_accessor :default_queue_name, default: "default"
|
11
10
|
|
12
11
|
# Specifies the name of the queue to process the job on.
|
@@ -49,13 +48,14 @@ module ActiveJob
|
|
49
48
|
def queue_name_from_part(part_name) #:nodoc:
|
50
49
|
queue_name = part_name || default_queue_name
|
51
50
|
name_parts = [queue_name_prefix.presence, queue_name]
|
52
|
-
name_parts.compact.join(queue_name_delimiter)
|
51
|
+
-name_parts.compact.join(queue_name_delimiter)
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
55
|
included do
|
57
56
|
class_attribute :queue_name, instance_accessor: false, default: -> { self.class.default_queue_name }
|
58
57
|
class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
|
58
|
+
class_attribute :queue_name_prefix
|
59
59
|
end
|
60
60
|
|
61
61
|
# Returns the name of the queue the job will be run on.
|
data/lib/active_job/railtie.rb
CHANGED
@@ -34,6 +34,10 @@ module ActiveJob
|
|
34
34
|
ActiveSupport.on_load(:action_dispatch_integration_test) do
|
35
35
|
include ActiveJob::TestHelper
|
36
36
|
end
|
37
|
+
|
38
|
+
ActiveSupport.on_load(:active_record) do
|
39
|
+
self.destroy_association_async_job = ActiveRecord::DestroyAssociationAsyncJob
|
40
|
+
end
|
37
41
|
end
|
38
42
|
|
39
43
|
initializer "active_job.set_reloader_hook" do |app|
|
@@ -2,11 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveJob
|
4
4
|
module Serializers
|
5
|
-
class DateTimeSerializer <
|
6
|
-
def serialize(time)
|
7
|
-
super("value" => time.iso8601)
|
8
|
-
end
|
9
|
-
|
5
|
+
class DateTimeSerializer < TimeObjectSerializer # :nodoc:
|
10
6
|
def deserialize(hash)
|
11
7
|
DateTime.iso8601(hash["value"])
|
12
8
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class ModuleSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(constant)
|
7
|
+
super("value" => constant.name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
hash["value"].constantize
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def klass
|
16
|
+
Module
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -2,11 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveJob
|
4
4
|
module Serializers
|
5
|
-
class TimeSerializer <
|
6
|
-
def serialize(time)
|
7
|
-
super("value" => time.iso8601)
|
8
|
-
end
|
9
|
-
|
5
|
+
class TimeSerializer < TimeObjectSerializer # :nodoc:
|
10
6
|
def deserialize(hash)
|
11
7
|
Time.iso8601(hash["value"])
|
12
8
|
end
|
@@ -2,11 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveJob
|
4
4
|
module Serializers
|
5
|
-
class TimeWithZoneSerializer <
|
6
|
-
def serialize(time)
|
7
|
-
super("value" => time.iso8601)
|
8
|
-
end
|
9
|
-
|
5
|
+
class TimeWithZoneSerializer < TimeObjectSerializer # :nodoc:
|
10
6
|
def deserialize(hash)
|
11
7
|
Time.iso8601(hash["value"]).in_time_zone
|
12
8
|
end
|