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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -0
  3. data/.travis.yml +20 -5
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +9 -6
  6. data/Gemfile.lock +63 -51
  7. data/LICENSE +1 -1
  8. data/README.md +57 -25
  9. data/Rakefile +14 -13
  10. data/activejob-retry.gemspec +9 -12
  11. data/lib/active_job/retry.rb +69 -90
  12. data/lib/active_job/retry/constant_backoff_strategy.rb +50 -0
  13. data/lib/active_job/retry/constant_options_validator.rb +76 -0
  14. data/lib/active_job/retry/deserialize_monkey_patch.rb +17 -12
  15. data/lib/active_job/retry/errors.rb +6 -0
  16. data/lib/active_job/retry/variable_backoff_strategy.rb +34 -0
  17. data/lib/active_job/retry/variable_options_validator.rb +56 -0
  18. data/lib/active_job/retry/version.rb +1 -1
  19. data/spec/retry/constant_backoff_strategy_spec.rb +115 -0
  20. data/spec/retry/constant_options_validator_spec.rb +81 -0
  21. data/spec/retry/variable_backoff_strategy_spec.rb +121 -0
  22. data/spec/retry/variable_options_validator_spec.rb +83 -0
  23. data/spec/retry_spec.rb +114 -170
  24. data/spec/spec_helper.rb +3 -2
  25. data/test/adapters/backburner.rb +3 -0
  26. data/test/adapters/delayed_job.rb +7 -0
  27. data/test/adapters/inline.rb +1 -0
  28. data/test/adapters/qu.rb +3 -0
  29. data/test/adapters/que.rb +4 -0
  30. data/test/adapters/queue_classic.rb +2 -0
  31. data/test/adapters/resque.rb +2 -0
  32. data/test/adapters/sidekiq.rb +2 -0
  33. data/test/adapters/sneakers.rb +2 -0
  34. data/test/adapters/sucker_punch.rb +2 -0
  35. data/test/cases/adapter_test.rb +8 -0
  36. data/test/cases/argument_serialization_test.rb +84 -0
  37. data/test/cases/callbacks_test.rb +23 -0
  38. data/test/cases/job_serialization_test.rb +15 -0
  39. data/test/cases/logging_test.rb +114 -0
  40. data/test/cases/queue_naming_test.rb +102 -0
  41. data/test/cases/queuing_test.rb +44 -0
  42. data/test/cases/rescue_test.rb +35 -0
  43. data/test/cases/test_case_test.rb +14 -0
  44. data/test/cases/test_helper_test.rb +226 -0
  45. data/test/helper.rb +21 -0
  46. data/test/integration/queuing_test.rb +46 -0
  47. data/test/jobs/callback_job.rb +31 -0
  48. data/test/jobs/gid_job.rb +10 -0
  49. data/test/jobs/hello_job.rb +9 -0
  50. data/test/jobs/logging_job.rb +12 -0
  51. data/test/jobs/nested_job.rb +12 -0
  52. data/test/jobs/rescue_job.rb +31 -0
  53. data/test/models/person.rb +20 -0
  54. data/test/support/backburner/inline.rb +8 -0
  55. data/test/support/delayed_job/delayed/backend/test.rb +111 -0
  56. data/test/support/delayed_job/delayed/serialization/test.rb +0 -0
  57. data/test/support/integration/adapters/backburner.rb +38 -0
  58. data/test/support/integration/adapters/delayed_job.rb +20 -0
  59. data/test/support/integration/adapters/que.rb +37 -0
  60. data/test/support/integration/adapters/resque.rb +49 -0
  61. data/test/support/integration/adapters/sidekiq.rb +58 -0
  62. data/test/support/integration/dummy_app_template.rb +28 -0
  63. data/test/support/integration/helper.rb +30 -0
  64. data/test/support/integration/jobs_manager.rb +27 -0
  65. data/test/support/integration/test_case_helpers.rb +48 -0
  66. data/test/support/job_buffer.rb +19 -0
  67. data/test/support/que/inline.rb +9 -0
  68. metadata +60 -12
  69. data/lib/active_job-retry.rb +0 -14
  70. data/lib/active_job/retry/exponential_backoff.rb +0 -92
  71. data/lib/active_job/retry/exponential_options_validator.rb +0 -57
  72. data/lib/active_job/retry/invalid_configuration_error.rb +0 -6
  73. 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
@@ -0,0 +1,19 @@
1
+ module JobBuffer
2
+ class << self
3
+ def clear
4
+ values.clear
5
+ end
6
+
7
+ def add(value)
8
+ values << value
9
+ end
10
+
11
+ def values
12
+ @values ||= []
13
+ end
14
+
15
+ def last_value
16
+ values.last
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require 'que'
2
+
3
+ Que::Job.class_eval do
4
+ class << self; alias_method :original_enqueue, :enqueue; end
5
+ def self.enqueue(*args)
6
+ args.pop if args.last.is_a?(Hash)
7
+ self.run(*args)
8
+ end
9
+ 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.0.1
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: pry-byebug
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 a simple DSL for automatically retrying ActiveJobs when they
99
- fail, with exponential backoff.
98
+ activejob-retry provides automatic retry functionality for failed
99
+ ActiveJobs, with exponential backoff.
100
100
 
101
101
  Features:
102
102
 
103
- * (Should) work with any queue adapter that supports retries.
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/exponential_backoff.rb
127
- - lib/active_job/retry/exponential_options_validator.rb
128
- - lib/active_job/retry/invalid_configuration_error.rb
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
- homepage: http://github.com/isaacseymour/activejob-retry
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 retrying DSL for ActiveJob
204
+ summary: Automatic retry functionality for ActiveJob.
157
205
  test_files: []
158
206
  has_rdoc: false
@@ -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