activejob-retry 0.0.1 → 0.1.1
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/.rubocop.yml +22 -0
- data/.travis.yml +20 -5
- data/CHANGELOG.md +3 -0
- data/Gemfile +9 -6
- data/Gemfile.lock +63 -51
- data/LICENSE +1 -1
- data/README.md +57 -25
- data/Rakefile +14 -13
- data/activejob-retry.gemspec +9 -12
- data/lib/active_job/retry.rb +69 -90
- data/lib/active_job/retry/constant_backoff_strategy.rb +50 -0
- data/lib/active_job/retry/constant_options_validator.rb +76 -0
- data/lib/active_job/retry/deserialize_monkey_patch.rb +17 -12
- data/lib/active_job/retry/errors.rb +6 -0
- data/lib/active_job/retry/variable_backoff_strategy.rb +34 -0
- data/lib/active_job/retry/variable_options_validator.rb +56 -0
- data/lib/active_job/retry/version.rb +1 -1
- data/spec/retry/constant_backoff_strategy_spec.rb +115 -0
- data/spec/retry/constant_options_validator_spec.rb +81 -0
- data/spec/retry/variable_backoff_strategy_spec.rb +121 -0
- data/spec/retry/variable_options_validator_spec.rb +83 -0
- data/spec/retry_spec.rb +114 -170
- data/spec/spec_helper.rb +3 -2
- data/test/adapters/backburner.rb +3 -0
- data/test/adapters/delayed_job.rb +7 -0
- data/test/adapters/inline.rb +1 -0
- data/test/adapters/qu.rb +3 -0
- data/test/adapters/que.rb +4 -0
- data/test/adapters/queue_classic.rb +2 -0
- data/test/adapters/resque.rb +2 -0
- data/test/adapters/sidekiq.rb +2 -0
- data/test/adapters/sneakers.rb +2 -0
- data/test/adapters/sucker_punch.rb +2 -0
- data/test/cases/adapter_test.rb +8 -0
- data/test/cases/argument_serialization_test.rb +84 -0
- data/test/cases/callbacks_test.rb +23 -0
- data/test/cases/job_serialization_test.rb +15 -0
- data/test/cases/logging_test.rb +114 -0
- data/test/cases/queue_naming_test.rb +102 -0
- data/test/cases/queuing_test.rb +44 -0
- data/test/cases/rescue_test.rb +35 -0
- data/test/cases/test_case_test.rb +14 -0
- data/test/cases/test_helper_test.rb +226 -0
- data/test/helper.rb +21 -0
- data/test/integration/queuing_test.rb +46 -0
- data/test/jobs/callback_job.rb +31 -0
- data/test/jobs/gid_job.rb +10 -0
- data/test/jobs/hello_job.rb +9 -0
- data/test/jobs/logging_job.rb +12 -0
- data/test/jobs/nested_job.rb +12 -0
- data/test/jobs/rescue_job.rb +31 -0
- data/test/models/person.rb +20 -0
- data/test/support/backburner/inline.rb +8 -0
- data/test/support/delayed_job/delayed/backend/test.rb +111 -0
- data/test/support/delayed_job/delayed/serialization/test.rb +0 -0
- data/test/support/integration/adapters/backburner.rb +38 -0
- data/test/support/integration/adapters/delayed_job.rb +20 -0
- data/test/support/integration/adapters/que.rb +37 -0
- data/test/support/integration/adapters/resque.rb +49 -0
- data/test/support/integration/adapters/sidekiq.rb +58 -0
- data/test/support/integration/dummy_app_template.rb +28 -0
- data/test/support/integration/helper.rb +30 -0
- data/test/support/integration/jobs_manager.rb +27 -0
- data/test/support/integration/test_case_helpers.rb +48 -0
- data/test/support/job_buffer.rb +19 -0
- data/test/support/que/inline.rb +9 -0
- metadata +60 -12
- data/lib/active_job-retry.rb +0 -14
- data/lib/active_job/retry/exponential_backoff.rb +0 -92
- data/lib/active_job/retry/exponential_options_validator.rb +0 -57
- data/lib/active_job/retry/invalid_configuration_error.rb +0 -6
- data/lib/active_job/retry/options_validator.rb +0 -84
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'sidekiq/cli'
|
2
|
+
require 'sidekiq/api'
|
3
|
+
|
4
|
+
module SidekiqJobsManager
|
5
|
+
|
6
|
+
def setup
|
7
|
+
ActiveJob::Base.queue_adapter = :sidekiq
|
8
|
+
unless can_run?
|
9
|
+
puts "Cannot run integration tests for sidekiq. To be able to run integration tests for sidekiq you need to install and start redis.\n"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear_jobs
|
15
|
+
Sidekiq::ScheduledSet.new.clear
|
16
|
+
Sidekiq::Queue.new("integration_tests").clear
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_workers
|
20
|
+
fork do
|
21
|
+
sidekiq = Sidekiq::CLI.instance
|
22
|
+
logfile = Rails.root.join("log/sidekiq.log").to_s
|
23
|
+
pidfile = Rails.root.join("tmp/sidekiq.pid").to_s
|
24
|
+
sidekiq.parse([ "--require", Rails.root.to_s,
|
25
|
+
"--queue", "integration_tests",
|
26
|
+
"--logfile", logfile,
|
27
|
+
"--pidfile", pidfile,
|
28
|
+
"--environment", "test",
|
29
|
+
"--concurrency", "1",
|
30
|
+
"--timeout", "1",
|
31
|
+
"--daemon",
|
32
|
+
])
|
33
|
+
require 'celluloid'
|
34
|
+
require 'sidekiq/scheduled'
|
35
|
+
Sidekiq.poll_interval = 0.5
|
36
|
+
Sidekiq::Scheduled.const_set :INITIAL_WAIT, 1
|
37
|
+
sidekiq.run
|
38
|
+
end
|
39
|
+
sleep 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def stop_workers
|
43
|
+
pidfile = Rails.root.join("tmp/sidekiq.pid").to_s
|
44
|
+
Process.kill 'TERM', File.open(pidfile).read.to_i
|
45
|
+
FileUtils.rm_f pidfile
|
46
|
+
rescue
|
47
|
+
end
|
48
|
+
|
49
|
+
def can_run?
|
50
|
+
begin
|
51
|
+
Sidekiq.redis(&:info)
|
52
|
+
Sidekiq.logger = nil
|
53
|
+
rescue
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
rake("db:create")
|
2
|
+
|
3
|
+
if ENV['AJADAPTER'] == 'delayed_job'
|
4
|
+
generate "delayed_job:active_record", "--quiet"
|
5
|
+
rake("db:migrate")
|
6
|
+
end
|
7
|
+
|
8
|
+
initializer 'activejob.rb', <<-CODE
|
9
|
+
require "#{File.expand_path("../jobs_manager.rb", __FILE__)}"
|
10
|
+
JobsManager.current_manager.setup
|
11
|
+
CODE
|
12
|
+
|
13
|
+
file 'app/jobs/test_job.rb', <<-CODE
|
14
|
+
class TestJob < ActiveJob::Base
|
15
|
+
include ActiveJob::Retry
|
16
|
+
|
17
|
+
queue_as :integration_tests
|
18
|
+
constant_retry limit: 2, delay: 3
|
19
|
+
|
20
|
+
def perform(x, fail_first = false)
|
21
|
+
raise "Failing first" if fail_first && retry_attempt == 1
|
22
|
+
|
23
|
+
File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f|
|
24
|
+
f.write x
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
CODE
|
@@ -0,0 +1,30 @@
|
|
1
|
+
puts "*** rake aj:integration:#{ENV['AJADAPTER']} ***\n"
|
2
|
+
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
ActiveJob::Base.queue_name_prefix = nil
|
5
|
+
|
6
|
+
require 'rails/generators/rails/app/app_generator'
|
7
|
+
|
8
|
+
dummy_app_path = Dir.mktmpdir + "/dummy"
|
9
|
+
dummy_app_template = File.expand_path("../dummy_app_template.rb", __FILE__)
|
10
|
+
args = Rails::Generators::ARGVScrubber.new(["new", dummy_app_path, "--skip-gemfile", "--skip-bundle",
|
11
|
+
"--skip-git", "--skip-spring", "-d", "sqlite3", "--skip-javascript", "--force", "--quiet",
|
12
|
+
"--template", dummy_app_template]).prepare!
|
13
|
+
Rails::Generators::AppGenerator.start args
|
14
|
+
|
15
|
+
require "#{dummy_app_path}/config/environment.rb"
|
16
|
+
|
17
|
+
ActiveRecord::Migrator.migrations_paths = [ Rails.root.join('db/migrate').to_s ]
|
18
|
+
require 'rails/test_help'
|
19
|
+
|
20
|
+
Rails.backtrace_cleaner.remove_silencers!
|
21
|
+
|
22
|
+
require_relative 'test_case_helpers'
|
23
|
+
ActiveSupport::TestCase.send(:include, TestCaseHelpers)
|
24
|
+
|
25
|
+
JobsManager.current_manager.start_workers
|
26
|
+
|
27
|
+
Minitest.after_run do
|
28
|
+
JobsManager.current_manager.stop_workers
|
29
|
+
JobsManager.current_manager.clear_jobs
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class JobsManager
|
2
|
+
@@managers = {}
|
3
|
+
attr :adapter_name
|
4
|
+
|
5
|
+
def self.current_manager
|
6
|
+
@@managers[ENV['AJADAPTER']] ||= new(ENV['AJADAPTER'])
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(adapter_name)
|
10
|
+
@adapter_name = adapter_name
|
11
|
+
require_relative "adapters/#{adapter_name}"
|
12
|
+
extend "#{adapter_name.camelize}JobsManager".constantize
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup
|
16
|
+
ActiveJob::Base.queue_adapter = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear_jobs
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_workers
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop_workers
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'support/integration/jobs_manager'
|
3
|
+
|
4
|
+
module TestCaseHelpers
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
self.use_transactional_fixtures = false
|
9
|
+
|
10
|
+
setup do
|
11
|
+
clear_jobs
|
12
|
+
@id = "AJ-#{SecureRandom.uuid}"
|
13
|
+
end
|
14
|
+
|
15
|
+
teardown do
|
16
|
+
clear_jobs
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def jobs_manager
|
23
|
+
JobsManager.current_manager
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_jobs
|
27
|
+
jobs_manager.clear_jobs
|
28
|
+
end
|
29
|
+
|
30
|
+
def adapter_is?(adapter)
|
31
|
+
ActiveJob::Base.queue_adapter.name.split("::").last.gsub(/Adapter$/, '').underscore==adapter.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def wait_for_jobs_to_finish_for(seconds=60)
|
35
|
+
begin
|
36
|
+
Timeout.timeout(seconds) do
|
37
|
+
while !job_executed do
|
38
|
+
sleep 0.25
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue Timeout::Error
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def job_executed
|
46
|
+
Dummy::Application.root.join("tmp/#{@id}").exist?
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activejob-retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Isaac Seymour
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rubocop
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - '>='
|
@@ -95,12 +95,12 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
description: |2
|
98
|
-
activejob-retry provides
|
99
|
-
|
98
|
+
activejob-retry provides automatic retry functionality for failed
|
99
|
+
ActiveJobs, with exponential backoff.
|
100
100
|
|
101
101
|
Features:
|
102
102
|
|
103
|
-
*
|
103
|
+
* Works with any queue adapter that supports retries.
|
104
104
|
* Whitelist/blacklist exceptions to retry on.
|
105
105
|
* Exponential backoff (varying the delay between retries).
|
106
106
|
* Light and easy to override retry logic.
|
@@ -112,6 +112,7 @@ extra_rdoc_files: []
|
|
112
112
|
files:
|
113
113
|
- .gitignore
|
114
114
|
- .rspec
|
115
|
+
- .rubocop.yml
|
115
116
|
- .travis.yml
|
116
117
|
- CHANGELOG.md
|
117
118
|
- Gemfile
|
@@ -120,17 +121,64 @@ files:
|
|
120
121
|
- README.md
|
121
122
|
- Rakefile
|
122
123
|
- activejob-retry.gemspec
|
123
|
-
- lib/active_job-retry.rb
|
124
124
|
- lib/active_job/retry.rb
|
125
|
+
- lib/active_job/retry/constant_backoff_strategy.rb
|
126
|
+
- lib/active_job/retry/constant_options_validator.rb
|
125
127
|
- lib/active_job/retry/deserialize_monkey_patch.rb
|
126
|
-
- lib/active_job/retry/
|
127
|
-
- lib/active_job/retry/
|
128
|
-
- lib/active_job/retry/
|
129
|
-
- lib/active_job/retry/options_validator.rb
|
128
|
+
- lib/active_job/retry/errors.rb
|
129
|
+
- lib/active_job/retry/variable_backoff_strategy.rb
|
130
|
+
- lib/active_job/retry/variable_options_validator.rb
|
130
131
|
- lib/active_job/retry/version.rb
|
132
|
+
- spec/retry/constant_backoff_strategy_spec.rb
|
133
|
+
- spec/retry/constant_options_validator_spec.rb
|
134
|
+
- spec/retry/variable_backoff_strategy_spec.rb
|
135
|
+
- spec/retry/variable_options_validator_spec.rb
|
131
136
|
- spec/retry_spec.rb
|
132
137
|
- spec/spec_helper.rb
|
133
|
-
|
138
|
+
- test/adapters/backburner.rb
|
139
|
+
- test/adapters/delayed_job.rb
|
140
|
+
- test/adapters/inline.rb
|
141
|
+
- test/adapters/qu.rb
|
142
|
+
- test/adapters/que.rb
|
143
|
+
- test/adapters/queue_classic.rb
|
144
|
+
- test/adapters/resque.rb
|
145
|
+
- test/adapters/sidekiq.rb
|
146
|
+
- test/adapters/sneakers.rb
|
147
|
+
- test/adapters/sucker_punch.rb
|
148
|
+
- test/cases/adapter_test.rb
|
149
|
+
- test/cases/argument_serialization_test.rb
|
150
|
+
- test/cases/callbacks_test.rb
|
151
|
+
- test/cases/job_serialization_test.rb
|
152
|
+
- test/cases/logging_test.rb
|
153
|
+
- test/cases/queue_naming_test.rb
|
154
|
+
- test/cases/queuing_test.rb
|
155
|
+
- test/cases/rescue_test.rb
|
156
|
+
- test/cases/test_case_test.rb
|
157
|
+
- test/cases/test_helper_test.rb
|
158
|
+
- test/helper.rb
|
159
|
+
- test/integration/queuing_test.rb
|
160
|
+
- test/jobs/callback_job.rb
|
161
|
+
- test/jobs/gid_job.rb
|
162
|
+
- test/jobs/hello_job.rb
|
163
|
+
- test/jobs/logging_job.rb
|
164
|
+
- test/jobs/nested_job.rb
|
165
|
+
- test/jobs/rescue_job.rb
|
166
|
+
- test/models/person.rb
|
167
|
+
- test/support/backburner/inline.rb
|
168
|
+
- test/support/delayed_job/delayed/backend/test.rb
|
169
|
+
- test/support/delayed_job/delayed/serialization/test.rb
|
170
|
+
- test/support/integration/adapters/backburner.rb
|
171
|
+
- test/support/integration/adapters/delayed_job.rb
|
172
|
+
- test/support/integration/adapters/que.rb
|
173
|
+
- test/support/integration/adapters/resque.rb
|
174
|
+
- test/support/integration/adapters/sidekiq.rb
|
175
|
+
- test/support/integration/dummy_app_template.rb
|
176
|
+
- test/support/integration/helper.rb
|
177
|
+
- test/support/integration/jobs_manager.rb
|
178
|
+
- test/support/integration/test_case_helpers.rb
|
179
|
+
- test/support/job_buffer.rb
|
180
|
+
- test/support/que/inline.rb
|
181
|
+
homepage: http://github.com/gocardless/activejob-retry
|
134
182
|
licenses:
|
135
183
|
- MIT
|
136
184
|
metadata: {}
|
@@ -153,6 +201,6 @@ rubyforge_project:
|
|
153
201
|
rubygems_version: 2.4.1
|
154
202
|
signing_key:
|
155
203
|
specification_version: 4
|
156
|
-
summary: Automatic
|
204
|
+
summary: Automatic retry functionality for ActiveJob.
|
157
205
|
test_files: []
|
158
206
|
has_rdoc: false
|
data/lib/active_job-retry.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'active_job'
|
2
|
-
require 'active_support'
|
3
|
-
|
4
|
-
require 'active_job/retry'
|
5
|
-
require 'active_job/retry/exponential_backoff'
|
6
|
-
require 'active_job/retry/invalid_configuration_error'
|
7
|
-
require 'active_job/retry/options_validator'
|
8
|
-
require 'active_job/retry/exponential_options_validator'
|
9
|
-
|
10
|
-
require 'active_job/retry/version'
|
11
|
-
|
12
|
-
unless ActiveJob::Base.method_defined?(:deserialize)
|
13
|
-
require 'active_job/retry/deserialize_monkey_patch'
|
14
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
module ActiveJob
|
2
|
-
module Retry
|
3
|
-
# If you want your job to retry on failure using a varying delay, simply
|
4
|
-
# extend your class with this module:
|
5
|
-
#
|
6
|
-
# class DeliverSMS < ActiveJob::Base
|
7
|
-
# extend ActiveJob::Retry::ExponentialBackoff
|
8
|
-
# queue_as :messages
|
9
|
-
#
|
10
|
-
# def perform(mobile_number, message)
|
11
|
-
# SmsService.deliver!(mobile_number, message)
|
12
|
-
# end
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# Easily do something custom:
|
16
|
-
#
|
17
|
-
# class DeliverSMS
|
18
|
-
# extend ActiveJob::Retry::ExponentialBackoff
|
19
|
-
# queue_as :messages
|
20
|
-
#
|
21
|
-
# # Options from ActiveJob::Retry can also be used
|
22
|
-
# retry_with strategy: [0, 60], # retry immediately, then after 60 seconds
|
23
|
-
# min_delay_multiplier: 0.8, # multiply the delay by a random number
|
24
|
-
# max_delay_multiplier: 1.5 # between 0.8 and 1.5
|
25
|
-
#
|
26
|
-
# def perform(mobile_number, message)
|
27
|
-
# SmsService.deliver!(mobile_number, message)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
#
|
31
|
-
module ExponentialBackoff
|
32
|
-
include ActiveJob::Retry
|
33
|
-
|
34
|
-
def self.included(base)
|
35
|
-
base.extend(ClassMethods)
|
36
|
-
end
|
37
|
-
|
38
|
-
module ClassMethods
|
39
|
-
include ActiveJob::Retry::ClassMethods
|
40
|
-
|
41
|
-
# Setup DSL
|
42
|
-
def backoff_with(options)
|
43
|
-
retry_with(options)
|
44
|
-
ExponentialOptionsValidator.new(options).validate!
|
45
|
-
|
46
|
-
@retry_limit = options[:limit] || options[:strategy].length if options[:strategy]
|
47
|
-
@backoff_strategy = options[:strategy] if options[:strategy]
|
48
|
-
@min_delay_multiplier = options[:min_delay_multiplier] if options[:min_delay_multiplier]
|
49
|
-
@max_delay_multiplier = options[:max_delay_multiplier] if options[:max_delay_multiplier]
|
50
|
-
end
|
51
|
-
|
52
|
-
############
|
53
|
-
# Defaults #
|
54
|
-
############
|
55
|
-
def retry_limit
|
56
|
-
@retry_limit ||= (backoff_strategy || []).length
|
57
|
-
end
|
58
|
-
|
59
|
-
def backoff_strategy
|
60
|
-
@backoff_strategy ||= nil
|
61
|
-
end
|
62
|
-
|
63
|
-
def min_delay_multiplier
|
64
|
-
@min_delay_multiplier ||= 1.0
|
65
|
-
end
|
66
|
-
|
67
|
-
def max_delay_multiplier
|
68
|
-
@max_delay_multiplier ||= 1.0
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
###############
|
73
|
-
# Retry logic #
|
74
|
-
###############
|
75
|
-
def retry_delay
|
76
|
-
delay = self.class.backoff_strategy[retry_attempt]
|
77
|
-
|
78
|
-
return (delay * self.class.max_delay_multiplier).to_i unless random_delay?
|
79
|
-
|
80
|
-
(delay * rand(random_delay_range)).to_i
|
81
|
-
end
|
82
|
-
|
83
|
-
def random_delay?
|
84
|
-
self.class.min_delay_multiplier != self.class.max_delay_multiplier
|
85
|
-
end
|
86
|
-
|
87
|
-
def random_delay_range
|
88
|
-
self.class.min_delay_multiplier..self.class.max_delay_multiplier
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|