aboutsummaryrefslogtreecommitdiffstats
path: root/activejob
diff options
context:
space:
mode:
Diffstat (limited to 'activejob')
-rw-r--r--activejob/CHANGELOG.md120
-rw-r--r--activejob/README.md9
-rw-r--r--activejob/activejob.gemspec2
-rw-r--r--activejob/lib/active_job/core.rb7
-rw-r--r--activejob/lib/active_job/enqueuing.rb2
-rw-r--r--activejob/lib/active_job/exceptions.rb19
-rw-r--r--activejob/lib/active_job/gem_version.rb4
-rw-r--r--activejob/lib/active_job/logging.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters.rb6
-rw-r--r--activejob/lib/active_job/queue_name.rb20
-rw-r--r--activejob/lib/active_job/test_helper.rb18
-rw-r--r--activejob/test/cases/exceptions_test.rb45
-rw-r--r--activejob/test/cases/job_serialization_test.rb11
-rw-r--r--activejob/test/cases/logging_test.rb2
-rw-r--r--activejob/test/cases/test_helper_test.rb54
-rw-r--r--activejob/test/helper.rb2
16 files changed, 183 insertions, 140 deletions
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 0ed937f4cc..2f0d72ccb9 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,121 +1,3 @@
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-* Return false instead of the job instance when `enqueue` is aborted.
- This will be the behavior in Rails 6.1 but it can be controlled now with
- `config.active_job.return_false_on_aborted_enqueue`.
-
- *Kir Shatrov*
-
-* Keep executions for each specific declaration
-
- Each `retry_on` declaration has now its own specific executions counter. Before it was
- shared between all executions of a job.
-
- *Alberto Almagro*
-
-* Allow all assertion helpers that have a `only` and `except` keyword to accept
- Procs.
-
- *Edouard Chin*
-
-* Restore HashWithIndifferentAccess support to ActiveJob::Arguments.deserialize.
-
- *Gannon McGibbon*
-
-* Include deserialized arguments in job instances returned from
- `assert_enqueued_with` and `assert_performed_with`
-
- *Alan Wu*
-
-* Allow `assert_enqueued_with`/`assert_performed_with` methods to accept
- a proc for the `args` argument. This is useful to check if only a subset of arguments
- matches your expectations.
-
- *Edouard Chin*
-
-* `ActionDispatch::IntegrationTest` includes `ActiveJob::TestHelper` module by default.
-
- *Ricardo Díaz*
-
-* Added `enqueue_retry.active_job`, `retry_stopped.active_job`, and `discard.active_job` hooks.
-
- *steves*
-
-* Allow `assert_performed_with` to be called without a block.
-
- *bogdanvlviv*
-
-* Execution of `assert_performed_jobs`, and `assert_no_performed_jobs`
- without a block should respect passed `:except`, `:only`, and `:queue` options.
-
- *bogdanvlviv*
-
-* Allow `:queue` option to job assertions and helpers.
-
- *bogdanvlviv*
-
-* Allow `perform_enqueued_jobs` to be called without a block.
-
- Performs all of the jobs that have been enqueued up to this point in the test.
-
- *Kevin Deisz*
-
-* Move `enqueue`/`enqueue_at` notifications to an around callback.
-
- Improves timing accuracy over the old after callback by including
- time spent writing to the adapter's IO implementation.
-
- *Zach Kemp*
-
-* Allow call `assert_enqueued_with` with no block.
-
- Example:
- ```
- def test_assert_enqueued_with
- MyJob.perform_later(1,2,3)
- assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low')
-
- MyJob.set(wait_until: Date.tomorrow.noon).perform_later
- assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
- end
- ```
-
- *bogdanvlviv*
-
-* Allow passing multiple exceptions to `retry_on`, and `discard_on`.
-
- *George Claghorn*
-
-* Pass the error instance as the second parameter of block executed by `discard_on`.
-
- Fixes #32853.
-
- *Yuji Yaginuma*
-
-* Remove support for Qu gem.
-
- Reasons are that the Qu gem wasn't compatible since Rails 5.1,
- gem development was stopped in 2014 and maintainers have
- confirmed its demise. See issue #32273
-
- *Alberto Almagro*
-
-* Add support for timezones to Active Job.
-
- Record what was the current timezone in effect when the job was
- enqueued and then restore when the job is executed in same way
- that the current locale is recorded and restored.
-
- *Andrew White*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-* Add support to define custom argument serializers.
-
- *Evgenii Pecherkin*, *Rafael Mendonça França*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activejob/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activejob/CHANGELOG.md) for previous changes.
diff --git a/activejob/README.md b/activejob/README.md
index a2a5289ab7..462d319992 100644
--- a/activejob/README.md
+++ b/activejob/README.md
@@ -1,4 +1,4 @@
-# Active Job -- Make work happen later
+# Active Job – Make work happen later
Active Job is a framework for declaring jobs and making them run on a variety
of queuing backends. These jobs can be everything from regularly scheduled
@@ -17,12 +17,13 @@ about API differences between Delayed Job and Resque. Picking your queuing
backend becomes more of an operational concern, then. And you'll be able to
switch between them without having to rewrite your jobs.
+You can read more about Active Job in the [Active Job Basics](https://edgeguides.rubyonrails.org/active_job_basics.html) guide.
## Usage
To learn how to use your preferred queuing backend see its adapter
documentation at
-[ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
+[ActiveJob::QueueAdapters](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
Declare a job like so:
@@ -86,7 +87,7 @@ by default has been mixed into Active Record classes.
Active Job has built-in adapters for multiple queuing backends (Sidekiq,
Resque, Delayed Job and others). To get an up-to-date list of the adapters
-see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
+see the API Documentation for [ActiveJob::QueueAdapters](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
**Please note:** We are not accepting pull requests for new adapters. We
encourage library authors to provide an ActiveJob adapter as part of
@@ -121,7 +122,7 @@ Active Job is released under the MIT license:
API documentation is at:
-* http://api.rubyonrails.org
+* https://api.rubyonrails.org
Bug reports for the Ruby on Rails project can be filed here:
diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec
index c3c0447d8e..aeffe55af6 100644
--- a/activejob/activejob.gemspec
+++ b/activejob/activejob.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.author = "David Heinemeier Hansson"
s.email = "david@loudthinking.com"
- s.homepage = "http://rubyonrails.org"
+ s.homepage = "https://rubyonrails.org"
s.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.md", "lib/**/*"]
s.require_path = "lib"
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index 487cdd6d38..283125698d 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -40,6 +40,9 @@ module ActiveJob
# Timezone to be used during the job.
attr_accessor :timezone
+ # Track when a job was enqueued
+ attr_accessor :enqueued_at
+
# These methods will be included into any Active Job object, adding
# helpers for de/serialization and creation of job instances.
module ClassMethods
@@ -97,7 +100,8 @@ module ActiveJob
"executions" => executions,
"exception_executions" => exception_executions,
"locale" => I18n.locale.to_s,
- "timezone" => Time.zone.try(:name)
+ "timezone" => Time.zone.try(:name),
+ "enqueued_at" => Time.now.utc.iso8601
}
end
@@ -137,6 +141,7 @@ module ActiveJob
self.exception_executions = job_data["exception_executions"]
self.locale = job_data["locale"] || I18n.locale.to_s
self.timezone = job_data["timezone"] || Time.zone.try(:name)
+ self.enqueued_at = job_data["enqueued_at"]
end
private
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index 5609d13f5f..c5ee76a69e 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -67,7 +67,7 @@ module ActiveJob
false
else
ActiveSupport::Deprecation.warn(
- "Rails 6.0 will return false when the enqueuing is aborted. Make sure your code doesn't depend on it" \
+ "Rails 6.1 will return false when the enqueuing is aborted. Make sure your code doesn't depend on it" \
" returning the instance of the job and set `config.active_job.return_false_on_aborted_enqueue = true`" \
" to remove the deprecations."
)
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index 9e00942a1c..8e83246303 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -49,12 +49,10 @@ module ActiveJob
# end
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
rescue_from(*exceptions) do |error|
- # Guard against jobs that were persisted before we started having individual executions counters per retry_on
- self.exception_executions ||= {}
- self.exception_executions[exceptions.to_s] = (exception_executions[exceptions.to_s] || 0) + 1
+ executions = executions_for(exceptions)
- if exception_executions[exceptions.to_s] < attempts
- retry_job wait: determine_delay(wait), queue: queue, priority: priority, error: error
+ if executions < attempts
+ retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions), queue: queue, priority: priority, error: error
else
if block_given?
instrument :retry_stopped, error: error do
@@ -123,7 +121,7 @@ module ActiveJob
end
private
- def determine_delay(seconds_or_duration_or_algorithm)
+ def determine_delay(seconds_or_duration_or_algorithm:, executions:)
case seconds_or_duration_or_algorithm
when :exponentially_longer
(executions**4) + 2
@@ -146,5 +144,14 @@ module ActiveJob
ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
end
+
+ def executions_for(exceptions)
+ if exception_executions
+ exception_executions[exceptions.to_s] = (exception_executions[exceptions.to_s] || 0) + 1
+ else
+ # Guard against jobs that were persisted before we started having individual executions counters per retry_on
+ executions
+ end
+ end
end
end
diff --git a/activejob/lib/active_job/gem_version.rb b/activejob/lib/active_job/gem_version.rb
index 2b894397e3..224ae9072a 100644
--- a/activejob/lib/active_job/gem_version.rb
+++ b/activejob/lib/active_job/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveJob
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta1"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index 416be83c24..1134e718a8 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -70,7 +70,7 @@ module ActiveJob
def perform_start(event)
info do
job = event.payload[:job]
- "Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)}" + args_info(job)
+ "Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} enqueued at #{job.enqueued_at}" + args_info(job)
end
end
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 525e79e302..a4b7eb86f1 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -10,11 +10,11 @@ module ActiveJob
# * {Que}[https://github.com/chanks/que]
# * {queue_classic}[https://github.com/QueueClassic/queue_classic]
# * {Resque}[https://github.com/resque/resque]
- # * {Sidekiq}[http://sidekiq.org]
+ # * {Sidekiq}[https://sidekiq.org]
# * {Sneakers}[https://github.com/jondot/sneakers]
# * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
- # * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
- # * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
+ # * {Active Job Async Job}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
+ # * {Active Job Inline}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
# * Please Note: We are not accepting pull requests for new adapters. See the {README}[link:files/activejob/README_md.html] for more details.
#
# === Backends Features
diff --git a/activejob/lib/active_job/queue_name.rb b/activejob/lib/active_job/queue_name.rb
index 7bb1e35181..de259261de 100644
--- a/activejob/lib/active_job/queue_name.rb
+++ b/activejob/lib/active_job/queue_name.rb
@@ -18,6 +18,26 @@ module ActiveJob
# post.to_feed!
# end
# end
+ #
+ # Can be given a block that will evaluate in the context of the job
+ # allowing +self.arguments+ to be accessed so that a dynamic queue name
+ # can be applied:
+ #
+ # class PublishToFeedJob < ApplicationJob
+ # queue_as do
+ # post = self.arguments.first
+ #
+ # if post.paid?
+ # :paid_feeds
+ # else
+ # :feeds
+ # end
+ # end
+ #
+ # def perform(post)
+ # post.to_feed!
+ # end
+ # end
def queue_as(part_name = nil, &block)
if block_given?
self.queue_name = block
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index f03780b91e..463020a332 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -353,7 +353,7 @@ module ActiveJob
#
#
# The +args+ argument also accepts a proc which will get passed the actual
- # job's arguments. Your proc needs to returns a boolean value determining if
+ # job's arguments. Your proc needs to return a boolean value determining if
# the job's arguments matches your expectation. This is useful to check only
# for a subset of arguments.
#
@@ -426,7 +426,7 @@ module ActiveJob
# end
#
# The +args+ argument also accepts a proc which will get passed the actual
- # job's arguments. Your proc needs to returns a boolean value determining if
+ # job's arguments. Your proc needs to return a boolean value determining if
# the job's arguments matches your expectation. This is useful to check only
# for a subset of arguments.
#
@@ -631,6 +631,20 @@ module ActiveJob
def prepare_args_for_assertion(args)
args.dup.tap do |arguments|
arguments[:at] = arguments[:at].to_f if arguments[:at]
+ arguments[:args] = round_time_arguments(arguments[:args]) if arguments[:args]
+ end
+ end
+
+ def round_time_arguments(argument)
+ case argument
+ when Time, ActiveSupport::TimeWithZone, DateTime
+ argument.change(usec: 0)
+ when Hash
+ argument.transform_values { |value| round_time_arguments(value) }
+ when Array
+ argument.map { |element| round_time_arguments(element) }
+ else
+ argument
end
end
diff --git a/activejob/test/cases/exceptions_test.rb b/activejob/test/cases/exceptions_test.rb
index c88162bf58..1f07b7b294 100644
--- a/activejob/test/cases/exceptions_test.rb
+++ b/activejob/test/cases/exceptions_test.rb
@@ -140,6 +140,26 @@ class ExceptionsTest < ActiveSupport::TestCase
], JobBuffer.values
end
+ test "use individual execution timers when calculating retry delay" do
+ travel_to Time.now
+
+ exceptions_to_raise = %w(ExponentialWaitTenAttemptsError CustomWaitTenAttemptsError ExponentialWaitTenAttemptsError CustomWaitTenAttemptsError)
+
+ RetryJob.perform_later exceptions_to_raise, 5, :log_scheduled_at
+
+ assert_equal [
+ "Raised ExponentialWaitTenAttemptsError for the 1st time",
+ "Next execution scheduled at #{(Time.now + 3.seconds).to_f}",
+ "Raised CustomWaitTenAttemptsError for the 2nd time",
+ "Next execution scheduled at #{(Time.now + 2.seconds).to_f}",
+ "Raised ExponentialWaitTenAttemptsError for the 3rd time",
+ "Next execution scheduled at #{(Time.now + 18.seconds).to_f}",
+ "Raised CustomWaitTenAttemptsError for the 4th time",
+ "Next execution scheduled at #{(Time.now + 4.seconds).to_f}",
+ "Successfully completed job"
+ ], JobBuffer.values
+ end
+
test "successfully retry job throwing one of two retryable exceptions" do
RetryJob.perform_later "SecondRetryableErrorOfTwo", 3
@@ -159,6 +179,31 @@ class ExceptionsTest < ActiveSupport::TestCase
assert_equal ["Raised ActiveJob::DeserializationError for the 5 time"], JobBuffer.values
end
+ test "running a job enqueued by AJ 5.2" do
+ job = RetryJob.new("DefaultsError", 6)
+ job.exception_executions = nil # This is how jobs from Rails 5.2 will look
+
+ assert_raises DefaultsError do
+ job.enqueue
+ end
+
+ assert_equal 5, JobBuffer.values.count
+ end
+
+ test "running a job enqueued and attempted under AJ 5.2" do
+ job = RetryJob.new("DefaultsError", 6)
+
+ # Fake 4 previous executions under AJ 5.2
+ job.exception_executions = nil
+ job.executions = 4
+
+ assert_raises DefaultsError do
+ job.enqueue
+ end
+
+ assert_equal ["Raised DefaultsError for the 5th time"], JobBuffer.values
+ end
+
private
def adapter_skips_scheduling?(queue_adapter)
[
diff --git a/activejob/test/cases/job_serialization_test.rb b/activejob/test/cases/job_serialization_test.rb
index 86f3651564..c1cec1f1d6 100644
--- a/activejob/test/cases/job_serialization_test.rb
+++ b/activejob/test/cases/job_serialization_test.rb
@@ -61,4 +61,15 @@ class JobSerializationTest < ActiveSupport::TestCase
assert_equal "Hawaii", job.serialize["timezone"]
end
end
+
+ test "serialize stores the enqueued_at time" do
+ h1 = HelloJob.new
+ type = h1.serialize["enqueued_at"].class
+ assert_equal String, type
+
+ h2 = HelloJob.deserialize(h1.serialize)
+ # We should be able to parse a timestamp
+ type = Time.parse(h2.enqueued_at).class
+ assert_equal Time, type
+ end
end
diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb
index 6154ba301d..acd37456c9 100644
--- a/activejob/test/cases/logging_test.rb
+++ b/activejob/test/cases/logging_test.rb
@@ -115,6 +115,8 @@ class LoggingTest < ActiveSupport::TestCase
perform_enqueued_jobs do
LoggingJob.perform_later "Dummy"
assert_match(/Performing LoggingJob \(Job ID: .*?\) from .*? with arguments:.*Dummy/, @logger.messages)
+
+ assert_match(/enqueued at /, @logger.messages)
assert_match(/Dummy, here is it: Dummy/, @logger.messages)
assert_match(/Performed LoggingJob \(Job ID: .*?\) from .*? in .*ms/, @logger.messages)
end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 4d934df31b..32471cfacc 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -581,6 +581,33 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_enqueued_with_time
+ now = Time.now
+ args = [{ argument1: [now] }]
+
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: [now])
+ end
+ end
+
+ def test_assert_enqueued_with_date_time
+ now = DateTime.now
+ args = [{ argument1: [now] }]
+
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: [now])
+ end
+ end
+
+ def test_assert_enqueued_with_time_with_zone
+ now = Time.now.in_time_zone("Tokyo")
+ args = [{ argument1: [now] }]
+
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: [now])
+ end
+ end
+
def test_assert_enqueued_with_with_no_block_args
assert_raise ArgumentError do
NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
@@ -1681,6 +1708,33 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_with_time
+ now = Time.now
+ args = [{ argument1: { now: now }, argument2: now }]
+
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: { now: now }, argument2: now)
+ end
+ end
+
+ def test_assert_performed_with_date_time
+ now = DateTime.now
+ args = [{ argument1: { now: now }, argument2: now }]
+
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: { now: now }, argument2: now)
+ end
+ end
+
+ def test_assert_performed_with_time_with_zone
+ now = Time.now.in_time_zone("Tokyo")
+ args = [{ argument1: { now: now }, argument2: now }]
+
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: { now: now }, argument2: now)
+ end
+ end
+
def test_assert_performed_with_with_global_id_args
ricardo = Person.new(9)
assert_performed_with(job: HelloJob, args: [ricardo]) do
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index 694232d7ef..d400210fef 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -16,3 +16,5 @@ else
end
require "active_support/testing/autorun"
+
+require_relative "../../tools/test_common"