activejob-retry 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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