activejob 7.1.4 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f58475b83cd4283bd3a28b829c173ee56d4a2a2b6c9d5735ff65c10d4ae0288c
4
- data.tar.gz: 60b1508fb23737f07e7b57bc904cd8fd68f2e5e3c74b93a61a3327627cec396a
3
+ metadata.gz: 598d84beb0c743d639d0e0e6e152484a1695a82a4eeba838761755cb19602d9b
4
+ data.tar.gz: 7127ad30ef29ca6792aad06cdc13df5644dd9d3edb623860d6e65e0309d58006
5
5
  SHA512:
6
- metadata.gz: e50fc69706f890dfcafe2f8ed1b87fbc13ffb766d5f7b994d3478c1a7bca132a48b65cf92aa4c28ae8d01024f53a6ec1338d2517f08759d332c572f77778bc59
7
- data.tar.gz: 7c7e86f171769cd24147410f8b148fed781b50271caa1c1fc72721af82a7b7cc4c1bbdef4b2741e887a87b8b5dfbd8aa59a8350c5024196a314a18b3e111c4a7
6
+ metadata.gz: c8cbfae1df6df13a75a277e51853ba3989fffdd29182f941c69d9d545f22aea966d9445f442681da4f272aa0c6e0fe4bd31f99489f99990ac2369ec688ddc94e
7
+ data.tar.gz: 5922b67c28a35052b2fa02c68b827689404622468dcacccf77326bf0bc4ea54d3e0f1d6db24f56737fefef6341f1b1d9a3b38b8acd4bd3add1d5b255fb6e722e
data/CHANGELOG.md CHANGED
@@ -1,290 +1,67 @@
1
- ## Rails 7.1.4 (August 22, 2024) ##
1
+ ## Rails 7.2.0.beta1 (May 29, 2024) ##
2
2
 
3
- * Register autoload for `ActiveJob::Arguments`.
3
+ * All tests now respect the `active_job.queue_adapter` config.
4
4
 
5
- *Rafael Mendonça França*
6
-
7
-
8
- ## Rails 7.1.3.4 (June 04, 2024) ##
9
-
10
- * No changes.
11
-
12
-
13
- ## Rails 7.1.3.3 (May 16, 2024) ##
14
-
15
- * No changes.
16
-
17
-
18
- ## Rails 7.1.3.2 (February 21, 2024) ##
19
-
20
- * No changes.
21
-
22
-
23
- ## Rails 7.1.3.1 (February 21, 2024) ##
24
-
25
- * No changes.
26
-
27
-
28
- ## Rails 7.1.3 (January 16, 2024) ##
29
-
30
- * Do not trigger immediate loading of `ActiveJob::Base` when loading `ActiveJob::TestHelper`.
31
-
32
- *Maxime Réty*
33
-
34
- * Preserve the serialized timezone when deserializing `ActiveSupport::TimeWithZone` arguments.
35
-
36
- *Joshua Young*
37
-
38
- * Fix ActiveJob arguments serialization to correctly serialize String subclasses having custom serializers.
39
-
40
- *fatkodima*
41
-
42
-
43
- ## Rails 7.1.2 (November 10, 2023) ##
44
-
45
- * No changes.
46
-
47
-
48
- ## Rails 7.1.1 (October 11, 2023) ##
49
-
50
- * Don't log enqueuing details when the job wasn't enqueued.
51
-
52
- *Dustin Brown*
53
-
54
-
55
- ## Rails 7.1.0 (October 05, 2023) ##
56
-
57
- * No changes.
58
-
59
-
60
- ## Rails 7.1.0.rc2 (October 01, 2023) ##
61
-
62
- * Make sure `scheduled_at` is a Time object when asserting enqueued jobs.
63
-
64
- *Rafael Mendonça França*
65
-
66
-
67
- ## Rails 7.1.0.rc1 (September 27, 2023) ##
68
-
69
- * Set `scheduled_at` attribute as a Time object instead of epoch seconds, and serialize and deserialize the value
70
- when enqueued. Assigning a numeric/epoch value to scheduled_at= is deprecated; use a Time object instead.
71
-
72
- Deserializes `enqueued_at` as a Time instead of ISO8601 String.
73
-
74
- *Ben Sheldon*
75
-
76
- * Clarify the backoff strategy for the recommended `:wait` option when retrying jobs
77
-
78
- `wait: :exponentially_longer` is waiting polynomially longer, so it is now recommended to use `wait: :polynomially_longer` to keep the same behavior.
79
-
80
- *Victor Mours*
81
-
82
-
83
- ## Rails 7.1.0.beta1 (September 13, 2023) ##
84
-
85
- * Fix Active Job log message to correctly report a job failed to enqueue
86
- when the adapter raises an `ActiveJob::EnqueueError`.
87
-
88
- *Ben Sheldon*
89
-
90
- * Add `after_discard` method.
91
-
92
- This method lets job authors define a block which will be run when a job is about to be discarded. For example:
93
-
94
- ```ruby
95
- class AfterDiscardJob < ActiveJob::Base
96
- after_discard do |job, exception|
97
- Rails.logger.info("#{job.class} raised an exception: #{exception}")
98
- end
99
-
100
- def perform
101
- raise StandardError
102
- end
103
- end
104
- ```
105
-
106
- The above job will run the block passed to `after_discard` after the job is discarded. The exception will
107
- still be raised after the block has been run.
5
+ Previously if you had set `config.active_job.queue_adapter` in your `config/application.rb`
6
+ or `config/environments/test.rb` file, the adapter you selected was previously not used consistently
7
+ across all tests. In some tests your adapter would be used, but other tests would use the `TestAdapter`.
108
8
 
109
- *Rob Cardy*
9
+ In Rails 7.2, all tests will respect the `queue_adapter` config if provided. If no config is provided,
10
+ the `TestAdapter` will continue to be used.
110
11
 
111
- * Fix deserialization of ActiveSupport::Duration
112
-
113
- Previously, a deserialized Duration would return an array from Duration#parts.
114
- It will now return a hash just like a regular Duration.
115
-
116
- This also fixes an error when trying to add or subtract from a deserialized Duration
117
- (eg `duration + 1.year`).
118
-
119
- *Jonathan del Strother*
120
-
121
- * `perform_enqueued_jobs` is now compatible with all Active Job adapters
122
-
123
- This means that methods that depend on it, like Action Mailer's `assert_emails`,
124
- will work correctly even if the test adapter is not used.
12
+ See [#48585](https://github.com/rails/rails/pull/48585) for more details.
125
13
 
126
14
  *Alex Ghiculescu*
127
15
 
128
- * Allow queue adapters to provide a custom name by implementing `queue_adapter_name`
129
-
130
- *Sander Verdonschot*
131
-
132
- * Log background job enqueue callers
133
-
134
- Add `verbose_enqueue_logs` configuration option to display the caller
135
- of background job enqueue in the log to help with debugging.
136
-
137
- Example log line:
138
-
139
- ```
140
- Enqueued AvatarThumbnailsJob (Job ID: ab528951-41fb-4c48-9129-3171791c27d6) to Sidekiq(default) with arguments: 1092412064
141
- ↳ app/models/user.rb:421:in `generate_avatar_thumbnails'
142
- ```
143
-
144
- Enabled in development only for new and upgraded applications. Not recommended for use
145
- in the production environment since it relies on Ruby's `Kernel#caller` which is fairly slow.
146
-
147
- *fatkodima*
148
-
149
- * Set `provider_job_id` for Backburner jobs
16
+ * Make Active Job transaction aware when used conjointly with Active Record.
150
17
 
151
- *Cameron Matheson*
152
-
153
- * Add `perform_all_later` to enqueue multiple jobs at once
154
-
155
- This adds the ability to bulk enqueue jobs, without running callbacks, by
156
- passing multiple jobs or an array of jobs. For example:
18
+ A common mistake with Active Job is to enqueue jobs from inside a transaction,
19
+ causing them to potentially be picked and ran by another process, before the
20
+ transaction is committed, which may result in various errors.
157
21
 
158
22
  ```ruby
159
- ActiveJob.perform_all_later(MyJob.new("hello", 42), MyJob.new("world", 0))
160
-
161
- user_jobs = User.pluck(:id).map { |id| UserJob.new(user_id: id) }
162
- ActiveJob.perform_all_later(user_jobs)
23
+ Topic.transaction do
24
+ topic = Topic.create(...)
25
+ NewTopicNotificationJob.perform_later(topic)
26
+ end
163
27
  ```
164
28
 
165
- This can greatly reduce the number of round-trips to the queue datastore.
166
- For queue adapters that do not implement the new `enqueue_all` method, we
167
- fall back to enqueuing jobs individually. The Sidekiq adapter implements
168
- `enqueue_all` with `push_bulk`.
169
-
170
- This method does not use the existing `enqueue.active_job` event, but adds a
171
- new event `enqueue_all.active_job`.
172
-
173
- *Sander Verdonschot*
174
-
175
- * Don't double log the `job` when using `ActiveRecord::QueryLog`
176
-
177
- Previously if you set `config.active_record.query_log_tags` to an array that included
178
- `:job`, the job name would get logged twice. This bug has been fixed.
179
-
180
- *Alex Ghiculescu*
181
-
182
- * Add support for Sidekiq's transaction-aware client
183
-
184
- *Jonathan del Strother*
185
-
186
- * Remove QueAdapter from Active Job.
187
-
188
- After maintaining Active Job QueAdapter by Rails and Que side
189
- to support Ruby 3 keyword arguments and options provided as top level keywords,
190
- it is quite difficult to maintain it this way.
191
-
192
- Active Job Que adapter can be included in the future version of que gem itself.
29
+ Now Active Job will automatically defer the enqueuing to after the transaction is committed,
30
+ and drop the job if the transaction is rolled back.
193
31
 
194
- *Yasuo Honda*
195
-
196
- * Fix BigDecimal (de)serialization for adapters using JSON.
197
-
198
- Previously, BigDecimal was listed as not needing a serializer. However,
199
- when used with an adapter storing the job arguments as JSON, it would get
200
- serialized as a simple String, resulting in deserialization also producing
201
- a String (instead of a BigDecimal).
202
-
203
- By using a serializer, we ensure the round trip is safe.
204
-
205
- To ensure applications using BigDecimal job arguments are not subject to
206
- race conditions during deployment (where a replica running a version of
207
- Rails without BigDecimalSerializer fails to deserialize an argument
208
- serialized with it), `ActiveJob.use_big_decimal_serializer` is disabled by
209
- default, and can be set to true in a following deployment..
210
-
211
- *Sam Bostock*
212
-
213
- * Preserve full-precision `enqueued_at` timestamps for serialized jobs,
214
- allowing more accurate reporting of how long a job spent waiting in the
215
- queue before it was performed.
216
-
217
- Retains IS08601 format compatibility.
218
-
219
- *Jeremy Daer*
220
-
221
- * Add `--parent` option to job generator to specify parent class of job.
222
-
223
- Example:
224
-
225
- `bin/rails g job process_payment --parent=payment_job` generates:
32
+ Various queue implementations can choose to disable this behavior, and users can disable it,
33
+ or force it on a per job basis:
226
34
 
227
35
  ```ruby
228
- class ProcessPaymentJob < PaymentJob
229
- # ...
36
+ class NewTopicNotificationJob < ApplicationJob
37
+ self.enqueue_after_transaction_commit = :never # or `:always` or `:default`
230
38
  end
231
39
  ```
232
40
 
233
- *Gannon McGibbon*
234
-
235
- * Add more detailed description to job generator.
236
-
237
- *Gannon McGibbon*
238
-
239
- * `perform.active_job` notification payloads now include `:db_runtime`, which
240
- is the total time (in ms) taken by database queries while performing a job.
241
- This value can be used to better understand how a job's time is spent.
242
-
243
- *Jonathan Hefner*
41
+ *Jean Boussier*, *Cristian Bica*
244
42
 
245
- * Update `ActiveJob::QueueAdapters::QueAdapter` to remove deprecation warning.
246
-
247
- Remove a deprecation warning introduced in que 1.2 to prepare for changes in
248
- que 2.0 necessary for Ruby 3 compatibility.
249
-
250
- *Damir Zekic* and *Adis Hasovic*
43
+ * Do not trigger immediate loading of `ActiveJob::Base` when loading `ActiveJob::TestHelper`.
251
44
 
252
- * Add missing `bigdecimal` require in `ActiveJob::Arguments`
45
+ *Maxime Réty*
253
46
 
254
- Could cause `uninitialized constant ActiveJob::Arguments::BigDecimal (NameError)`
255
- when loading Active Job in isolation.
47
+ * Preserve the serialized timezone when deserializing `ActiveSupport::TimeWithZone` arguments.
256
48
 
257
- *Jean Boussier*
49
+ *Joshua Young*
258
50
 
259
- * Allow testing `discard_on/retry_on ActiveJob::DeserializationError`
51
+ * Remove deprecated `:exponentially_longer` value for the `:wait` in `retry_on`.
260
52
 
261
- Previously in `perform_enqueued_jobs`, `deserialize_arguments_if_needed`
262
- was called before calling `perform_now`. When a record no longer exists
263
- and is serialized using GlobalID this led to raising
264
- an `ActiveJob::DeserializationError` before reaching `perform_now` call.
265
- This behavior makes difficult testing the job `discard_on/retry_on` logic.
53
+ *Rafael Mendonça França*
266
54
 
267
- Now `deserialize_arguments_if_needed` call is postponed to when `perform_now`
268
- is called.
55
+ * Remove deprecated support to set numeric values to `scheduled_at` attribute.
269
56
 
270
- Example:
57
+ *Rafael Mendonça França*
271
58
 
272
- ```ruby
273
- class UpdateUserJob < ActiveJob::Base
274
- discard_on ActiveJob::DeserializationError
59
+ * Deprecate `Rails.application.config.active_job.use_big_decimal_serialize`.
275
60
 
276
- def perform(user)
277
- # ...
278
- end
279
- end
61
+ *Rafael Mendonça França*
280
62
 
281
- # In the test
282
- User.destroy_all
283
- assert_nothing_raised do
284
- perform_enqueued_jobs only: UpdateUserJob
285
- end
286
- ```
63
+ * Remove deprecated primitive serializer for `BigDecimal` arguments.
287
64
 
288
- *Jacopo Beschi*
65
+ *Rafael Mendonça França*
289
66
 
290
- Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activejob/CHANGELOG.md) for previous changes.
67
+ Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/activejob/CHANGELOG.md) for previous changes.
@@ -101,15 +101,6 @@ module ActiveJob
101
101
  else
102
102
  if argument.respond_to?(:permitted?) && argument.respond_to?(:to_h)
103
103
  serialize_indifferent_hash(argument.to_h)
104
- elsif BigDecimal === argument && !ActiveJob.use_big_decimal_serializer
105
- ActiveJob.deprecator.warn(<<~MSG)
106
- Primitive serialization of BigDecimal job arguments is deprecated as it may serialize via .to_s using certain queue adapters.
107
- Enable config.active_job.use_big_decimal_serializer to use BigDecimalSerializer instead, which will be mandatory in Rails 7.2.
108
-
109
- Note that if your application has multiple replicas, you should only enable this setting after successfully deploying your app to Rails 7.1 first.
110
- This will ensure that during your deployment all replicas are capable of deserializing arguments serialized with BigDecimalSerializer.
111
- MSG
112
- argument
113
104
  else
114
105
  Serializers.serialize(argument)
115
106
  end
@@ -120,8 +111,6 @@ module ActiveJob
120
111
  case argument
121
112
  when nil, true, false, String, Integer, Float
122
113
  argument
123
- when BigDecimal # BigDecimal may have been legacy serialized; Remove in 7.2
124
- argument
125
114
  when Array
126
115
  argument.map { |arg| deserialize_argument(arg) }
127
116
  when Hash
@@ -15,9 +15,6 @@ module ActiveJob
15
15
  # * <tt>before_perform</tt>
16
16
  # * <tt>around_perform</tt>
17
17
  # * <tt>after_perform</tt>
18
- #
19
- # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
20
- #
21
18
  module Callbacks
22
19
  extend ActiveSupport::Concern
23
20
  include ActiveSupport::Callbacks
@@ -13,9 +13,7 @@ module ActiveJob
13
13
  attr_writer :serialized_arguments
14
14
 
15
15
  # Time when the job should be performed
16
- attr_reader :scheduled_at
17
-
18
- attr_reader :_scheduled_at_time # :nodoc:
16
+ attr_accessor :scheduled_at
19
17
 
20
18
  # Job Identifier
21
19
  attr_accessor :job_id
@@ -97,7 +95,6 @@ module ActiveJob
97
95
  @job_id = SecureRandom.uuid
98
96
  @queue_name = self.class.queue_name
99
97
  @scheduled_at = nil
100
- @_scheduled_at_time = nil
101
98
  @priority = self.class.priority
102
99
  @executions = 0
103
100
  @exception_executions = {}
@@ -120,7 +117,7 @@ module ActiveJob
120
117
  "locale" => I18n.locale.to_s,
121
118
  "timezone" => timezone,
122
119
  "enqueued_at" => Time.now.utc.iso8601(9),
123
- "scheduled_at" => _scheduled_at_time ? _scheduled_at_time.utc.iso8601(9) : nil,
120
+ "scheduled_at" => scheduled_at ? scheduled_at.utc.iso8601(9) : nil,
124
121
  }
125
122
  end
126
123
 
@@ -174,18 +171,6 @@ module ActiveJob
174
171
  self
175
172
  end
176
173
 
177
- def scheduled_at=(value)
178
- @_scheduled_at_time = if value&.is_a?(Numeric)
179
- ActiveJob.deprecator.warn(<<~MSG.squish)
180
- Assigning a numeric/epoch value to scheduled_at is deprecated. Use a Time object instead.
181
- MSG
182
- Time.at(value)
183
- else
184
- value
185
- end
186
- @scheduled_at = value
187
- end
188
-
189
174
  private
190
175
  def serialize_arguments_if_needed(arguments)
191
176
  if arguments_serialized?
@@ -211,7 +196,7 @@ module ActiveJob
211
196
  end
212
197
 
213
198
  def arguments_serialized?
214
- defined?(@serialized_arguments) && @serialized_arguments
199
+ @serialized_arguments
215
200
  end
216
201
  end
217
202
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module EnqueueAfterTransactionCommit # :nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ ##
9
+ # :singleton-method:
10
+ #
11
+ # Defines if enqueueing this job from inside an Active Record transaction
12
+ # automatically defers the enqueue to after the transaction commits.
13
+ #
14
+ # It can be set on a per job basis:
15
+ # - `:always` forces the job to be deferred.
16
+ # - `:never` forces the job to be queued immediately.
17
+ # - `:default` lets the queue adapter define the behavior (recommended).
18
+ class_attribute :enqueue_after_transaction_commit, instance_accessor: false, instance_predicate: false, default: :never
19
+ end
20
+
21
+ private
22
+ def raw_enqueue
23
+ after_transaction = case self.class.enqueue_after_transaction_commit
24
+ when :always
25
+ true
26
+ when :never
27
+ false
28
+ else # :default
29
+ queue_adapter.enqueue_after_transaction_commit?
30
+ end
31
+
32
+ if after_transaction
33
+ self.successfully_enqueued = true
34
+ ActiveRecord.after_all_transactions_commit do
35
+ self.successfully_enqueued = false
36
+ super
37
+ end
38
+ self
39
+ else
40
+ super
41
+ end
42
+ end
43
+ end
44
+ end
@@ -21,7 +21,7 @@ module ActiveJob
21
21
  adapter_jobs.each do |job|
22
22
  job.successfully_enqueued = false
23
23
  if job.scheduled_at
24
- queue_adapter.enqueue_at(job, job._scheduled_at_time.to_f)
24
+ queue_adapter.enqueue_at(job, job.scheduled_at.to_f)
25
25
  else
26
26
  queue_adapter.enqueue(job)
27
27
  end
@@ -50,9 +50,21 @@ module ActiveJob
50
50
  # custom serializers.
51
51
  #
52
52
  # Returns an instance of the job class queued with arguments available in
53
- # Job#arguments or false if the enqueue did not succeed.
53
+ # Job#arguments or +false+ if the enqueue did not succeed.
54
54
  #
55
55
  # After the attempted enqueue, the job will be yielded to an optional block.
56
+ #
57
+ # If Active Job is used conjointly with Active Record, and #perform_later is called
58
+ # inside an Active Record transaction, then the enqueue is implicitly deferred to after
59
+ # the transaction is committed, or dropped if it's rolled back. In such case #perform_later
60
+ # will return the job instance like if it was successfully enqueued, but will still return
61
+ # +false+ if a callback prevented the job from being enqueued.
62
+ #
63
+ # This behavior can be changed on a per job basis:
64
+ #
65
+ # class NotificationJob < ApplicationJob
66
+ # self.enqueue_after_transaction_commit = false
67
+ # end
56
68
  def perform_later(...)
57
69
  job = job_or_instantiate(...)
58
70
  enqueue_result = job.enqueue
@@ -63,7 +75,7 @@ module ActiveJob
63
75
  end
64
76
 
65
77
  private
66
- def job_or_instantiate(*args) # :doc:
78
+ def job_or_instantiate(*args, &_) # :doc:
67
79
  args.first.is_a?(self) ? args.first : new(*args)
68
80
  end
69
81
  ruby2_keywords(:job_or_instantiate)
@@ -89,8 +101,20 @@ module ActiveJob
89
101
  self.successfully_enqueued = false
90
102
 
91
103
  run_callbacks :enqueue do
104
+ raw_enqueue
105
+ end
106
+
107
+ if successfully_enqueued?
108
+ self
109
+ else
110
+ false
111
+ end
112
+ end
113
+
114
+ private
115
+ def raw_enqueue
92
116
  if scheduled_at
93
- queue_adapter.enqueue_at self, _scheduled_at_time.to_f
117
+ queue_adapter.enqueue_at self, scheduled_at.to_f
94
118
  else
95
119
  queue_adapter.enqueue self
96
120
  end
@@ -99,12 +123,5 @@ module ActiveJob
99
123
  rescue EnqueueError => e
100
124
  self.enqueue_error = e
101
125
  end
102
-
103
- if successfully_enqueued?
104
- self
105
- else
106
- false
107
- end
108
- end
109
126
  end
110
127
  end
@@ -21,7 +21,7 @@ module ActiveJob
21
21
  # You can also pass a block that'll be invoked if the retry attempts fail for custom logic rather than letting
22
22
  # the exception bubble up. This block is yielded with the job instance as the first and the error instance as the second parameter.
23
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
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
25
  # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if any.
26
26
  #
27
27
  # ==== Options
@@ -29,8 +29,8 @@ module ActiveJob
29
29
  # as a computing proc that takes the number of executions so far as an argument, or as a symbol reference of
30
30
  # <tt>:polynomially_longer</tt>, which applies the wait algorithm of <tt>((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2</tt>
31
31
  # (first wait ~3s, then ~18s, then ~83s, etc)
32
- # * <tt>:attempts</tt> - Re-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
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.
34
34
  # * <tt>:queue</tt> - Re-enqueues the job on a different queue
35
35
  # * <tt>:priority</tt> - Re-enqueues the job with a different priority
36
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)
@@ -60,12 +60,6 @@ module ActiveJob
60
60
  # end
61
61
  # end
62
62
  def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil, jitter: JITTER_DEFAULT)
63
- if wait == :exponentially_longer
64
- ActiveJob.deprecator.warn(<<~MSG.squish)
65
- `wait: :exponentially_longer` will actually wait polynomially longer and is therefore deprecated.
66
- Prefer `wait: :polynomially_longer` to avoid confusion and keep the same behavior.
67
- MSG
68
- end
69
63
  rescue_from(*exceptions) do |error|
70
64
  executions = executions_for(exceptions)
71
65
  if attempts == :unlimited || executions < attempts
@@ -90,7 +84,7 @@ module ActiveJob
90
84
  #
91
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.
92
86
  #
93
- # `retry_on` and `discard_on` handlers are searched from bottom to top, and up the class hierarchy. The handler of the first class for
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
94
88
  # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if any.
95
89
  #
96
90
  # ==== Example
@@ -168,7 +162,7 @@ module ActiveJob
168
162
  jitter = jitter == JITTER_DEFAULT ? self.class.retry_jitter : (jitter || 0.0)
169
163
 
170
164
  case seconds_or_duration_or_algorithm
171
- when :exponentially_longer, :polynomially_longer
165
+ when :polynomially_longer
172
166
  # This delay uses a polynomial backoff strategy, which was previously misnamed as exponential
173
167
  delay = executions**4
174
168
  delay_jitter = determine_jitter_for_delay(delay, jitter)
@@ -8,9 +8,9 @@ module ActiveJob
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 1
12
- TINY = 4
13
- PRE = nil
11
+ MINOR = 2
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -15,7 +15,8 @@ module ActiveJob
15
15
  # = Active Job Queue adapter
16
16
  #
17
17
  # The +ActiveJob::QueueAdapter+ module is used to load the
18
- # correct adapter. The default queue adapter is the +:async+ queue.
18
+ # correct adapter. The default queue adapter is +:async+,
19
+ # which loads the ActiveJob::QueueAdapters::AsyncAdapter.
19
20
  module QueueAdapter # :nodoc:
20
21
  extend ActiveSupport::Concern
21
22
 
@@ -24,21 +25,21 @@ module ActiveJob
24
25
  class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false
25
26
 
26
27
  delegate :queue_adapter, to: :class
27
-
28
- self.queue_adapter = :async
29
28
  end
30
29
 
31
30
  # Includes the setter method for changing the active queue adapter.
32
31
  module ClassMethods
33
32
  # Returns the backend queue provider. The default queue adapter
34
- # is the +:async+ queue. See QueueAdapters for more information.
33
+ # is +:async+. See QueueAdapters for more information.
35
34
  def queue_adapter
35
+ self.queue_adapter = :async if _queue_adapter.nil?
36
36
  _queue_adapter
37
37
  end
38
38
 
39
39
  # Returns string denoting the name of the configured queue adapter.
40
40
  # By default returns <tt>"async"</tt>.
41
41
  def queue_adapter_name
42
+ self.queue_adapter = :async if _queue_adapter_name.nil?
42
43
  _queue_adapter_name
43
44
  end
44
45
 
@@ -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
@@ -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)
@@ -14,7 +14,7 @@ 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
19
  response = Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
20
20
  job.provider_job_id = response[:id] if response.is_a?(Hash)
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -17,7 +17,7 @@ module ActiveJob
17
17
  # To use Sidekiq set the queue_adapter config to +:sidekiq+.
18
18
  #
19
19
  # Rails.application.config.active_job.queue_adapter = :sidekiq
20
- class SidekiqAdapter
20
+ class SidekiqAdapter < AbstractAdapter
21
21
  def enqueue(job) # :nodoc:
22
22
  job.provider_job_id = JobWrapper.set(
23
23
  wrapped: job.class,
@@ -17,7 +17,7 @@ module ActiveJob
17
17
  # To use Sneakers set the queue_adapter config to +:sneakers+.
18
18
  #
19
19
  # Rails.application.config.active_job.queue_adapter = :sneakers
20
- class SneakersAdapter
20
+ class SneakersAdapter < AbstractAdapter
21
21
  def initialize
22
22
  @monitor = Monitor.new
23
23
  end
@@ -17,7 +17,7 @@ module ActiveJob
17
17
  # To use Sucker Punch set the queue_adapter config to +:sucker_punch+.
18
18
  #
19
19
  # Rails.application.config.active_job.queue_adapter = :sucker_punch
20
- class SuckerPunchAdapter
20
+ class SuckerPunchAdapter < AbstractAdapter
21
21
  def enqueue(job) # :nodoc:
22
22
  if JobWrapper.respond_to?(:perform_async)
23
23
  # sucker_punch 2.0 API
@@ -11,10 +11,18 @@ module ActiveJob
11
11
  # To use the test adapter set +queue_adapter+ config to +:test+.
12
12
  #
13
13
  # Rails.application.config.active_job.queue_adapter = :test
14
- class TestAdapter
15
- attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at)
14
+ class TestAdapter < AbstractAdapter
15
+ attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at, :enqueue_after_transaction_commit)
16
16
  attr_writer(:enqueued_jobs, :performed_jobs)
17
17
 
18
+ def initialize(enqueue_after_transaction_commit: true)
19
+ @enqueue_after_transaction_commit = enqueue_after_transaction_commit
20
+ end
21
+
22
+ def enqueue_after_transaction_commit? # :nodoc:
23
+ @enqueue_after_transaction_commit
24
+ end
25
+
18
26
  # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
19
27
  def enqueued_jobs
20
28
  @enqueued_jobs ||= []
@@ -114,6 +114,7 @@ module ActiveJob
114
114
  module QueueAdapters
115
115
  extend ActiveSupport::Autoload
116
116
 
117
+ autoload :AbstractAdapter
117
118
  autoload :AsyncAdapter
118
119
  autoload :InlineAdapter
119
120
  autoload :BackburnerAdapter
@@ -25,9 +25,23 @@ module ActiveJob
25
25
  end
26
26
  end
27
27
 
28
+ initializer "active_job.enqueue_after_transaction_commit" do |app|
29
+ if config.active_job.key?(:enqueue_after_transaction_commit)
30
+ enqueue_after_transaction_commit = config.active_job.delete(:enqueue_after_transaction_commit)
31
+
32
+ ActiveSupport.on_load(:active_record) do
33
+ ActiveSupport.on_load(:active_job) do
34
+ include EnqueueAfterTransactionCommit
35
+
36
+ ActiveJob::Base.enqueue_after_transaction_commit = enqueue_after_transaction_commit
37
+ end
38
+ end
39
+ end
40
+ end
41
+
28
42
  initializer "active_job.set_configs" do |app|
29
43
  options = app.config.active_job
30
- options.queue_adapter ||= :async
44
+ options.queue_adapter ||= (Rails.env.test? ? :test : :async)
31
45
 
32
46
  config.after_initialize do
33
47
  options.each do |k, v|
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "singleton"
4
+
3
5
  module ActiveJob
4
6
  module Serializers
5
7
  # Base class for serializing and deserializing custom objects.
@@ -39,10 +39,13 @@ module ActiveJob
39
39
  end
40
40
 
41
41
  def before_setup # :nodoc:
42
- test_adapter = queue_adapter_for_test
43
-
42
+ queue_adapter_specific_to_this_test_class = queue_adapter_for_test
44
43
  queue_adapter_changed_jobs.each do |klass|
45
- klass.enable_test_adapter(test_adapter)
44
+ if queue_adapter_specific_to_this_test_class
45
+ klass.enable_test_adapter(queue_adapter_specific_to_this_test_class)
46
+ elsif klass._queue_adapter.nil?
47
+ klass.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
48
+ end
46
49
  end
47
50
 
48
51
  clear_enqueued_jobs
@@ -61,7 +64,6 @@ module ActiveJob
61
64
  # Override this method to specify a different adapter. The adapter must
62
65
  # implement the same interface as ActiveJob::QueueAdapters::TestAdapter.
63
66
  def queue_adapter_for_test
64
- ActiveJob::QueueAdapters::TestAdapter.new
65
67
  end
66
68
 
67
69
  # Asserts that the number of enqueued jobs matches the given number.
@@ -118,6 +120,8 @@ module ActiveJob
118
120
  # end
119
121
  # end
120
122
  def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
123
+ require_active_job_test_adapter!("assert_enqueued_jobs")
124
+
121
125
  if block_given?
122
126
  original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
123
127
 
@@ -180,6 +184,8 @@ module ActiveJob
180
184
  #
181
185
  # assert_enqueued_jobs 0, &block
182
186
  def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
187
+ require_active_job_test_adapter!("assert_no_enqueued_jobs")
188
+
183
189
  assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
184
190
  end
185
191
 
@@ -270,6 +276,8 @@ module ActiveJob
270
276
  # end
271
277
  # end
272
278
  def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
279
+ require_active_job_test_adapter!("assert_performed_jobs")
280
+
273
281
  if block_given?
274
282
  original_count = performed_jobs.size
275
283
 
@@ -338,6 +346,8 @@ module ActiveJob
338
346
  #
339
347
  # assert_performed_jobs 0, &block
340
348
  def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
349
+ require_active_job_test_adapter!("assert_no_performed_jobs")
350
+
341
351
  assert_performed_jobs 0, only: only, except: except, queue: queue, &block
342
352
  end
343
353
 
@@ -394,6 +404,8 @@ module ActiveJob
394
404
  # end
395
405
  # end
396
406
  def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, priority: nil, &block)
407
+ require_active_job_test_adapter!("assert_enqueued_with")
408
+
397
409
  expected = { job: job, args: args, at: at, queue: queue, priority: priority }.compact
398
410
  expected_args = prepare_args_for_assertion(expected)
399
411
  potential_matches = []
@@ -496,6 +508,8 @@ module ActiveJob
496
508
  # end
497
509
  # end
498
510
  def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, priority: nil, &block)
511
+ require_active_job_test_adapter!("assert_performed_with")
512
+
499
513
  expected = { job: job, args: args, at: at, queue: queue, priority: priority }.compact
500
514
  expected_args = prepare_args_for_assertion(expected)
501
515
  potential_matches = []
@@ -604,7 +618,10 @@ module ActiveJob
604
618
  # If queue_adapter_for_test is overridden to return a different adapter,
605
619
  # +perform_enqueued_jobs+ will merely execute the block.
606
620
  def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
607
- return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
621
+ unless block_given?
622
+ require_active_job_test_adapter!("perform_enqueued_jobs (without a block)")
623
+ return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at)
624
+ end
608
625
 
609
626
  return _assert_nothing_raised_or_warn("perform_enqueued_jobs", &block) unless using_test_adapter?
610
627
 
@@ -646,6 +663,12 @@ module ActiveJob
646
663
  end
647
664
 
648
665
  private
666
+ def require_active_job_test_adapter!(method)
667
+ unless using_test_adapter?
668
+ raise ArgumentError.new("#{method} requires the Active Job test adapter, you're using #{queue_adapter.class.name}.")
669
+ end
670
+ end
671
+
649
672
  def using_test_adapter?
650
673
  queue_adapter.is_a?(ActiveJob::QueueAdapters::TestAdapter)
651
674
  end
data/lib/active_job.rb CHANGED
@@ -39,6 +39,7 @@ module ActiveJob
39
39
  autoload :Arguments
40
40
  autoload :DeserializationError, "active_job/arguments"
41
41
  autoload :SerializationError, "active_job/arguments"
42
+ autoload :EnqueueAfterTransactionCommit
42
43
 
43
44
  eager_autoload do
44
45
  autoload :Serializers
@@ -48,16 +49,20 @@ module ActiveJob
48
49
  autoload :TestCase
49
50
  autoload :TestHelper
50
51
 
51
- ##
52
- # :singleton-method:
53
- # If false, \Rails will preserve the legacy serialization of BigDecimal job arguments as Strings.
54
- # If true, \Rails will use the new BigDecimalSerializer to (de)serialize BigDecimal losslessly.
55
- # Legacy serialization will be removed in \Rails 7.2, along with this config.
56
- singleton_class.attr_accessor :use_big_decimal_serializer
57
- self.use_big_decimal_serializer = false
52
+ def self.use_big_decimal_serializer
53
+ ActiveJob.deprecator.warn <<-WARNING.squish
54
+ Rails.application.config.active_job.use_big_decimal_serializer is deprecated and will be removed in Rails 7.3.
55
+ WARNING
56
+ end
57
+
58
+ def self.use_big_decimal_serializer=(value)
59
+ ActiveJob.deprecator.warn <<-WARNING.squish
60
+ Rails.application.config.active_job.use_big_decimal_serializer is deprecated and will be removed in Rails 7.3.
61
+ WARNING
62
+ end
58
63
 
59
64
  ##
60
- # :singleton-method:
65
+ # :singleton-method: verbose_enqueue_logs
61
66
  #
62
67
  # Specifies if the methods calling background job enqueue should be logged below
63
68
  # their relevant enqueue log lines. Defaults to false.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activejob
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.4
4
+ version: 7.2.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-22 00:00:00.000000000 Z
11
+ date: 2024-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.1.4
19
+ version: 7.2.0.beta1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.1.4
26
+ version: 7.2.0.beta1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: globalid
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -54,6 +54,7 @@ files:
54
54
  - lib/active_job/configured_job.rb
55
55
  - lib/active_job/core.rb
56
56
  - lib/active_job/deprecator.rb
57
+ - lib/active_job/enqueue_after_transaction_commit.rb
57
58
  - lib/active_job/enqueuing.rb
58
59
  - lib/active_job/exceptions.rb
59
60
  - lib/active_job/execution.rb
@@ -63,6 +64,7 @@ files:
63
64
  - lib/active_job/logging.rb
64
65
  - lib/active_job/queue_adapter.rb
65
66
  - lib/active_job/queue_adapters.rb
67
+ - lib/active_job/queue_adapters/abstract_adapter.rb
66
68
  - lib/active_job/queue_adapters/async_adapter.rb
67
69
  - lib/active_job/queue_adapters/backburner_adapter.rb
68
70
  - lib/active_job/queue_adapters/delayed_job_adapter.rb
@@ -102,10 +104,10 @@ licenses:
102
104
  - MIT
103
105
  metadata:
104
106
  bug_tracker_uri: https://github.com/rails/rails/issues
105
- changelog_uri: https://github.com/rails/rails/blob/v7.1.4/activejob/CHANGELOG.md
106
- documentation_uri: https://api.rubyonrails.org/v7.1.4/
107
+ changelog_uri: https://github.com/rails/rails/blob/v7.2.0.beta1/activejob/CHANGELOG.md
108
+ documentation_uri: https://api.rubyonrails.org/v7.2.0.beta1/
107
109
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
108
- source_code_uri: https://github.com/rails/rails/tree/v7.1.4/activejob
110
+ source_code_uri: https://github.com/rails/rails/tree/v7.2.0.beta1/activejob
109
111
  rubygems_mfa_required: 'true'
110
112
  post_install_message:
111
113
  rdoc_options: []
@@ -115,14 +117,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
117
  requirements:
116
118
  - - ">="
117
119
  - !ruby/object:Gem::Version
118
- version: 2.7.0
120
+ version: 3.1.0
119
121
  required_rubygems_version: !ruby/object:Gem::Requirement
120
122
  requirements:
121
123
  - - ">="
122
124
  - !ruby/object:Gem::Version
123
125
  version: '0'
124
126
  requirements: []
125
- rubygems_version: 3.5.11
127
+ rubygems_version: 3.5.10
126
128
  signing_key:
127
129
  specification_version: 4
128
130
  summary: Job framework with pluggable queues.