rocketjob 5.4.1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +175 -5
  3. data/bin/rocketjob_batch_perf +1 -1
  4. data/bin/rocketjob_perf +1 -1
  5. data/lib/rocket_job/batch/categories.rb +345 -0
  6. data/lib/rocket_job/batch/io.rb +174 -106
  7. data/lib/rocket_job/batch/model.rb +20 -68
  8. data/lib/rocket_job/batch/performance.rb +19 -7
  9. data/lib/rocket_job/batch/statistics.rb +34 -12
  10. data/lib/rocket_job/batch/throttle_running_workers.rb +2 -6
  11. data/lib/rocket_job/batch/worker.rb +31 -26
  12. data/lib/rocket_job/batch.rb +3 -1
  13. data/lib/rocket_job/category/base.rb +81 -0
  14. data/lib/rocket_job/category/input.rb +170 -0
  15. data/lib/rocket_job/category/output.rb +34 -0
  16. data/lib/rocket_job/cli.rb +25 -17
  17. data/lib/rocket_job/dirmon_entry.rb +23 -13
  18. data/lib/rocket_job/event.rb +1 -1
  19. data/lib/rocket_job/extensions/iostreams/path.rb +32 -0
  20. data/lib/rocket_job/extensions/mongoid/contextual/mongo.rb +2 -2
  21. data/lib/rocket_job/extensions/mongoid/factory.rb +4 -12
  22. data/lib/rocket_job/extensions/mongoid/stringified_symbol.rb +50 -0
  23. data/lib/rocket_job/extensions/psych/yaml_tree.rb +8 -0
  24. data/lib/rocket_job/extensions/rocket_job_adapter.rb +2 -2
  25. data/lib/rocket_job/jobs/conversion_job.rb +43 -0
  26. data/lib/rocket_job/jobs/dirmon_job.rb +25 -36
  27. data/lib/rocket_job/jobs/housekeeping_job.rb +11 -12
  28. data/lib/rocket_job/jobs/on_demand_batch_job.rb +24 -11
  29. data/lib/rocket_job/jobs/on_demand_job.rb +3 -4
  30. data/lib/rocket_job/jobs/performance_job.rb +3 -1
  31. data/lib/rocket_job/jobs/re_encrypt/relational_job.rb +103 -96
  32. data/lib/rocket_job/jobs/upload_file_job.rb +48 -8
  33. data/lib/rocket_job/lookup_collection.rb +69 -0
  34. data/lib/rocket_job/plugins/cron.rb +60 -20
  35. data/lib/rocket_job/plugins/job/model.rb +25 -50
  36. data/lib/rocket_job/plugins/job/persistence.rb +36 -0
  37. data/lib/rocket_job/plugins/job/throttle.rb +2 -2
  38. data/lib/rocket_job/plugins/job/throttle_running_jobs.rb +1 -1
  39. data/lib/rocket_job/plugins/job/worker.rb +2 -7
  40. data/lib/rocket_job/plugins/restart.rb +3 -103
  41. data/lib/rocket_job/plugins/state_machine.rb +4 -3
  42. data/lib/rocket_job/plugins/throttle_dependent_jobs.rb +37 -0
  43. data/lib/rocket_job/ractor_worker.rb +42 -0
  44. data/lib/rocket_job/server/model.rb +1 -1
  45. data/lib/rocket_job/sliced/bzip2_output_slice.rb +18 -19
  46. data/lib/rocket_job/sliced/compressed_slice.rb +3 -6
  47. data/lib/rocket_job/sliced/encrypted_bzip2_output_slice.rb +49 -0
  48. data/lib/rocket_job/sliced/encrypted_slice.rb +4 -6
  49. data/lib/rocket_job/sliced/input.rb +42 -54
  50. data/lib/rocket_job/sliced/slice.rb +12 -16
  51. data/lib/rocket_job/sliced/slices.rb +26 -11
  52. data/lib/rocket_job/sliced/writer/input.rb +46 -18
  53. data/lib/rocket_job/sliced/writer/output.rb +33 -45
  54. data/lib/rocket_job/sliced.rb +1 -74
  55. data/lib/rocket_job/subscribers/server.rb +1 -1
  56. data/lib/rocket_job/thread_worker.rb +46 -0
  57. data/lib/rocket_job/throttle_definitions.rb +7 -1
  58. data/lib/rocket_job/version.rb +1 -1
  59. data/lib/rocket_job/worker.rb +21 -55
  60. data/lib/rocket_job/worker_pool.rb +5 -7
  61. data/lib/rocketjob.rb +53 -43
  62. metadata +36 -28
  63. data/lib/rocket_job/batch/tabular/input.rb +0 -131
  64. data/lib/rocket_job/batch/tabular/output.rb +0 -65
  65. data/lib/rocket_job/batch/tabular.rb +0 -56
  66. data/lib/rocket_job/extensions/mongoid/remove_warnings.rb +0 -12
  67. data/lib/rocket_job/jobs/on_demand_batch_tabular_job.rb +0 -28
@@ -0,0 +1,69 @@
1
+ module RocketJob
2
+ class LookupCollection < Mongo::Collection
3
+ # Rapidly upload individual records in batches.
4
+ #
5
+ # Operates directly on a Mongo Collection to avoid the overhead of creating Mongoid objects
6
+ # for each and every row.
7
+ #
8
+ # Example:
9
+ # lookup_collection(:my_lookup).upload do |io|
10
+ # io << {id: 123, data: "first record"}
11
+ # io << {id: 124, data: "second record"}
12
+ # end
13
+ #
14
+ # input_category(:my_lookup).find(id: 123).first
15
+ def upload(batch_size: 10_000, &block)
16
+ BatchUploader.upload(batch_size: batch_size, &block)
17
+ end
18
+
19
+ # Looks up the value at the specified id.
20
+ # Returns [nil] if no record was found with the supplied id.
21
+ def lookup(id)
22
+ find(id: id).first
23
+ end
24
+
25
+ # Internal class for uploading records in batches
26
+ class BatchUploader
27
+ attr_reader :record_count
28
+
29
+ def self.upload(collection, **args)
30
+ writer = new(collection, **args)
31
+ yield(writer)
32
+ writer.record_count
33
+ ensure
34
+ writer&.close
35
+ end
36
+
37
+ def initialize(collection, batch_size:)
38
+ @batch_size = batch_size
39
+ @record_count = 0
40
+ @batch_count = 0
41
+ @documents = []
42
+ @collection = collection
43
+ end
44
+
45
+ def <<(record)
46
+ raise(ArgumentError, "Record must be a Hash") unless record.is_a?(Hash)
47
+
48
+ unless record.key?(:id) || record.key?("id") || record.key?("_id")
49
+ raise(ArgumentError, "Record must include an :id key")
50
+ end
51
+
52
+ @documents << record
53
+ @record_count += 1
54
+ @batch_count += 1
55
+ if @batch_count >= @batch_size
56
+ @collection.insert_many(@documents)
57
+ @documents.clear
58
+ @batch_count = 0
59
+ end
60
+
61
+ self
62
+ end
63
+
64
+ def close
65
+ @collection.insert_many(@documents) unless @documents.empty?
66
+ end
67
+ end
68
+ end
69
+ end
@@ -14,41 +14,81 @@ module RocketJob
14
14
  extend ActiveSupport::Concern
15
15
 
16
16
  included do
17
- include Restart
18
-
19
17
  field :cron_schedule, type: String, class_attribute: true, user_editable: true, copy_on_restart: true
20
18
 
19
+ # Whether to prevent another instance of this job from running with the exact _same_ cron schedule.
20
+ # Another job instance with a different `cron_schedule` string is permitted.
21
+ field :cron_singleton, type: Mongoid::Boolean, default: true, class_attribute: true, user_editable: true, copy_on_restart: true
22
+
23
+ # Whether to re-schedule the next job occurrence when this job starts, or when it is complete.
24
+ #
25
+ # `true`: Create a new scheduled instance of this job after it has started. (Default)
26
+ # - Ensures that the next scheduled instance is not missed because the current instance is still running.
27
+ # - Any changes to fields marked with `copy_on_restart` of `true` will be saved to the new scheduled instance
28
+ # _only_ if they were changed during an `after_start` callback.
29
+ # Changes to these during other callbacks or during the `perform` will not be saved to the new scheduled
30
+ # instance.
31
+ # - To prevent this job creating any new duplicate instances during subsequent processing,
32
+ # its `cron_schedule` is set to `nil`.
33
+ #
34
+ # `false`: Create a new scheduled instance of this job on `fail`, or `abort`.
35
+ # - Prevents the next scheduled instance from running or being scheduled while the current instance is
36
+ # still running.
37
+ # - Any changes to fields marked with `copy_on_restart` of `true` will be saved to the new scheduled instance
38
+ # at any time until after the job has failed, or is aborted.
39
+ # - To prevent this job creating any new duplicate instances during subsequent processing,
40
+ # its `cron_schedule` is set to `nil` after it fails or is aborted.
41
+ field :cron_after_start, type: Mongoid::Boolean, default: true, class_attribute: true, user_editable: true, copy_on_restart: true
42
+
21
43
  validates_each :cron_schedule do |record, attr, value|
22
44
  record.errors.add(attr, "Invalid cron_schedule: #{value.inspect}") if value && !Fugit::Cron.new(value)
23
45
  end
46
+ validate :rocket_job_cron_singleton_check
47
+
24
48
  before_save :rocket_job_cron_set_run_at
25
49
 
26
- private
50
+ after_start :rocket_job_cron_on_start
51
+ after_abort :rocket_job_cron_end_state
52
+ after_complete :rocket_job_cron_end_state
53
+ after_fail :rocket_job_cron_end_state
54
+ end
27
55
 
28
- # Prevent auto restart if this job does not have a cron schedule.
29
- # Overrides: RocketJob::Plugins::Restart#rocket_job_restart_new_instance
30
- def rocket_job_restart_new_instance
31
- return unless cron_schedule
56
+ def rocket_job_cron_set_run_at
57
+ return if cron_schedule.nil? || !(cron_schedule_changed? && !run_at_changed?)
32
58
 
33
- super
34
- end
59
+ self.run_at = Fugit::Cron.new(cron_schedule).next_time.to_utc_time
60
+ end
35
61
 
36
- # On failure:
37
- # - create a new instance scheduled to run in the future.
38
- # - clear out the `cron_schedule` so this instance will not schedule another instance to run on completion.
39
- # Overrides: RocketJob::Plugins::Restart#rocket_job_restart_abort
40
- def rocket_job_restart_abort
41
- return unless cron_schedule
62
+ private
42
63
 
43
- rocket_job_restart_new_instance
44
- update_attribute(:cron_schedule, nil)
64
+ def rocket_job_cron_on_start
65
+ return unless cron_schedule && cron_after_start
66
+
67
+ current_cron_schedule = cron_schedule
68
+ update_attribute(:cron_schedule, nil)
69
+ create_restart!(cron_schedule: current_cron_schedule)
70
+ end
71
+
72
+ def rocket_job_cron_end_state
73
+ return unless cron_schedule && !cron_after_start
74
+
75
+ current_cron_schedule = cron_schedule
76
+ update_attribute(:cron_schedule, nil)
77
+ create_restart!(cron_schedule: current_cron_schedule)
78
+ end
79
+
80
+ # Returns [true|false] whether another instance of this job with the same cron schedule is already active
81
+ def rocket_job_cron_duplicate?
82
+ self.class.with(read: {mode: :primary}) do |conn|
83
+ conn.where(:state.in => %i[queued running failed paused], :id.ne => id, cron_schedule: cron_schedule).exists?
45
84
  end
46
85
  end
47
86
 
48
- def rocket_job_cron_set_run_at
49
- return if cron_schedule.nil? || !(cron_schedule_changed? && !run_at_changed?)
87
+ # Prevent creation of a new job when another is running with the same cron schedule.
88
+ def rocket_job_cron_singleton_check
89
+ return if cron_schedule.nil? || completed? || aborted? || !rocket_job_cron_duplicate?
50
90
 
51
- self.run_at = Fugit::Cron.new(cron_schedule).next_time.to_utc_time
91
+ errors.add(:state, "Another instance of #{self.class.name} is already queued, running, failed, or paused with the same cron schedule: #{cron_schedule}")
52
92
  end
53
93
  end
54
94
  end
@@ -37,12 +37,10 @@ module RocketJob
37
37
  # arrives, then the current job will complete the current slices and process
38
38
  # the new higher priority job
39
39
  field :priority, type: Integer, default: 50, class_attribute: true, user_editable: true, copy_on_restart: true
40
+ validates_inclusion_of :priority, in: 1..100
40
41
 
41
42
  # When the job completes destroy it from both the database and the UI
42
- field :destroy_on_complete, type: Boolean, default: true, class_attribute: true, copy_on_restart: true
43
-
44
- # Whether to store the results from this job
45
- field :collect_output, type: Boolean, default: false, class_attribute: true
43
+ field :destroy_on_complete, type: Mongoid::Boolean, default: true, class_attribute: true, copy_on_restart: true
46
44
 
47
45
  # Run this job no earlier than this time
48
46
  field :run_at, type: Time, user_editable: true
@@ -54,14 +52,15 @@ module RocketJob
54
52
  # Can be used to reduce log noise, especially during high volume calls
55
53
  # For debugging a single job can be logged at a low level such as :trace
56
54
  # Levels supported: :trace, :debug, :info, :warn, :error, :fatal
57
- field :log_level, type: Symbol, class_attribute: true, user_editable: true, copy_on_restart: true
55
+ field :log_level, type: Mongoid::StringifiedSymbol, class_attribute: true, user_editable: true, copy_on_restart: true
56
+ validates_inclusion_of :log_level, in: SemanticLogger::LEVELS + [nil]
58
57
 
59
58
  #
60
59
  # Read-only attributes
61
60
  #
62
61
 
63
62
  # Current state, as set by the state machine. Do not modify this value directly.
64
- field :state, type: Symbol, default: :queued
63
+ field :state, type: Mongoid::StringifiedSymbol, default: :queued
65
64
 
66
65
  # When the job was created
67
66
  field :created_at, type: Time, default: -> { Time.now }
@@ -89,17 +88,12 @@ module RocketJob
89
88
  # Store the last exception for this job
90
89
  embeds_one :exception, class_name: "RocketJob::JobException"
91
90
 
92
- # Store the Hash result from this job if collect_output is true,
93
- # and the job returned actually returned a Hash, otherwise nil
94
- # Not applicable to SlicedJob jobs, since its output is stored in a
95
- # separate collection
96
- field :result, type: Hash
97
-
91
+ # Used when workers fetch jobs to work on.
98
92
  index({state: 1, priority: 1, _id: 1}, background: true)
93
+ # Used by Mission Control to display completed jobs sorted by completion.
94
+ index({completed_at: 1}, background: true)
99
95
 
100
96
  validates_presence_of :state, :failure_count, :created_at
101
- validates :priority, inclusion: 1..100
102
- validates :log_level, inclusion: SemanticLogger::LEVELS + [nil]
103
97
  end
104
98
 
105
99
  module ClassMethods
@@ -155,14 +149,8 @@ module RocketJob
155
149
 
156
150
  # Scope for queued jobs that can run now
157
151
  # I.e. Queued jobs excluding scheduled jobs
158
- if Mongoid::VERSION.to_f >= 7.1
159
- def queued_now
160
- queued.and(RocketJob::Job.where(run_at: nil).or(:run_at.lte => Time.now))
161
- end
162
- else
163
- def queued_now
164
- queued.or({run_at: nil}, :run_at.lte => Time.now)
165
- end
152
+ def queued_now
153
+ queued.and(RocketJob::Job.where(run_at: nil).or(:run_at.lte => Time.now))
166
154
  end
167
155
 
168
156
  # Defines all the fields that are accessible on the Document
@@ -183,43 +171,30 @@ module RocketJob
183
171
  #
184
172
  # @return [ Field ] The generated field
185
173
  def field(name, options)
186
- if options.delete(:user_editable) == true
187
- self.user_editable_fields += [name.to_sym] unless user_editable_fields.include?(name.to_sym)
174
+ if (options.delete(:user_editable) == true) && !user_editable_fields.include?(name.to_sym)
175
+ self.user_editable_fields += [name.to_sym]
188
176
  end
177
+
189
178
  if options.delete(:class_attribute) == true
190
179
  class_attribute(name, instance_accessor: false)
191
180
  public_send("#{name}=", options[:default]) if options.key?(:default)
192
181
  options[:default] = -> { self.class.public_send(name) }
193
182
  end
194
- if options.delete(:copy_on_restart) == true
195
- self.rocket_job_restart_attributes += [name.to_sym] unless rocket_job_restart_attributes.include?(name.to_sym)
183
+
184
+ if (options.delete(:copy_on_restart) == true) && !rocket_job_restart_attributes.include?(name.to_sym)
185
+ self.rocket_job_restart_attributes += [name.to_sym]
196
186
  end
197
- super(name, options)
198
- end
199
187
 
200
- # DEPRECATED
201
- def rocket_job
202
- warn "Replace calls to .rocket_job with calls to set class instance variables. For example: self.priority = 50"
203
- yield(self)
188
+ super(name, options)
204
189
  end
205
190
 
206
- # DEPRECATED
207
- def public_rocket_job_properties(*args)
208
- warn "Replace calls to .public_rocket_job_properties by adding `user_editable: true` option to the field declaration in #{name} for: #{args.inspect}"
209
- self.user_editable_fields += args.collect(&:to_sym)
191
+ # Builds this job instance from the supplied properties hash.
192
+ # Overridden by batch to support child objects.
193
+ def from_properties(properties)
194
+ new(properties)
210
195
  end
211
196
  end
212
197
 
213
- # Returns [true|false] whether to collect nil results from running this batch
214
- def collect_nil_output?
215
- collect_output? ? (collect_nil_output == true) : false
216
- end
217
-
218
- # Returns [true|false] whether to collect the results from running this batch
219
- def collect_output?
220
- collect_output == true
221
- end
222
-
223
198
  # Returns [Float] the number of seconds the job has taken
224
199
  # - Elapsed seconds to process the job from when a worker first started working on it
225
200
  # until now if still running, or until it was completed
@@ -282,7 +257,6 @@ module RocketJob
282
257
  # Returns [Hash] status of this job
283
258
  def as_json
284
259
  attrs = serializable_hash(methods: %i[seconds duration])
285
- attrs.delete("result") unless collect_output?
286
260
  attrs.delete("failure_count") unless failure_count.positive?
287
261
  if queued?
288
262
  attrs.delete("started_at")
@@ -319,16 +293,17 @@ module RocketJob
319
293
  h = as_json
320
294
  h.delete("seconds")
321
295
  h.dup.each_pair do |k, v|
322
- if v.is_a?(Time)
296
+ case v
297
+ when Time
323
298
  h[k] = v.in_time_zone(time_zone).to_s
324
- elsif v.is_a?(BSON::ObjectId)
299
+ when BSON::ObjectId
325
300
  h[k] = v.to_s
326
301
  end
327
302
  end
328
303
  h
329
304
  end
330
305
 
331
- # Returns [Boolean] whether the worker runs on a particular server.
306
+ # Returns [true|false] whether the worker runs on a particular server.
332
307
  def worker_on_server?(server_name)
333
308
  return false unless worker_name.present? && server_name.present?
334
309
 
@@ -70,6 +70,29 @@ module RocketJob
70
70
  end
71
71
  end
72
72
 
73
+ # Create a new instance of this job, copying across only the `copy_on_restart` attributes.
74
+ # Copy across input and output categories to new scheduled job so that all of the
75
+ # settings are remembered between instance. Example: slice_size
76
+ def create_restart!(**overrides)
77
+ if expired?
78
+ logger.info("Job has expired. Not creating a new instance.")
79
+ return
80
+ end
81
+
82
+ job_attrs = self.class.rocket_job_restart_attributes.each_with_object({}) do |attr, attrs|
83
+ attrs[attr] = send(attr)
84
+ end
85
+ job_attrs.merge!(overrides)
86
+
87
+ job = self.class.new(job_attrs)
88
+ job.input_categories = input_categories if respond_to?(:input_categories)
89
+ job.output_categories = output_categories if respond_to?(:output_categories)
90
+
91
+ job.save_with_retry!
92
+
93
+ logger.info("Created a new job instance: #{job.id}")
94
+ end
95
+
73
96
  # Set in-memory job to complete if `destroy_on_complete` and the job has been destroyed
74
97
  def reload
75
98
  return super unless destroy_on_complete
@@ -85,6 +108,19 @@ module RocketJob
85
108
  self
86
109
  end
87
110
  end
111
+
112
+ # Save with retry in case persistence takes a moment.
113
+ def save_with_retry!(retry_limit = 10, sleep_interval = 0.5)
114
+ count = 0
115
+ while count < retry_limit
116
+ return true if save
117
+
118
+ logger.info("Retrying to persist new scheduled instance: #{errors.messages.inspect}")
119
+ sleep(sleep_interval)
120
+ count += 1
121
+ end
122
+ save!
123
+ end
88
124
  end
89
125
  end
90
126
  end
@@ -48,7 +48,7 @@ module RocketJob
48
48
  # Note: Throttles are executed in the order they are defined.
49
49
  def define_throttle(method_name, filter: :throttle_filter_class)
50
50
  # Duplicate to prevent modifying parent class throttles
51
- definitions = rocket_job_throttles ? rocket_job_throttles.dup : ThrottleDefinitions.new
51
+ definitions = rocket_job_throttles ? rocket_job_throttles.deep_dup : ThrottleDefinitions.new
52
52
  definitions.add(method_name, filter)
53
53
  self.rocket_job_throttles = definitions
54
54
  end
@@ -57,7 +57,7 @@ module RocketJob
57
57
  def undefine_throttle(method_name)
58
58
  return unless rocket_job_throttles
59
59
 
60
- definitions = rocket_job_throttles.dup
60
+ definitions = rocket_job_throttles.deep_dup
61
61
  definitions.remove(method_name)
62
62
  self.rocket_job_throttles = definitions
63
63
  end
@@ -37,7 +37,7 @@ module RocketJob
37
37
 
38
38
  private
39
39
 
40
- # Returns [Boolean] whether the throttle for this job has been exceeded
40
+ # Returns [true|false] whether the throttle for this job has been exceeded
41
41
  def throttle_running_jobs_exceeded?
42
42
  return false unless throttle_running_jobs&.positive?
43
43
 
@@ -48,11 +48,11 @@ module RocketJob
48
48
  def perform_now
49
49
  raise(::Mongoid::Errors::Validations, self) unless valid?
50
50
 
51
- worker = RocketJob::Worker.new(inline: true)
51
+ worker = RocketJob::Worker.new
52
52
  start if may_start?
53
53
  # Re-Raise exceptions
54
54
  rocket_job_work(worker, true) if running?
55
- result
55
+ @rocket_job_output
56
56
  end
57
57
 
58
58
  def perform(*)
@@ -106,11 +106,6 @@ module RocketJob
106
106
  end
107
107
  end
108
108
 
109
- if collect_output?
110
- # Result must be a Hash, if not put it in a Hash
111
- self.result = @rocket_job_output.is_a?(Hash) ? @rocket_job_output : {"result" => @rocket_job_output}
112
- end
113
-
114
109
  if new_record? || destroyed?
115
110
  complete if may_complete?
116
111
  else
@@ -2,121 +2,21 @@ require "active_support/concern"
2
2
 
3
3
  module RocketJob
4
4
  module Plugins
5
- # Automatically starts a new instance of this job anytime it fails, aborts, or completes.
6
- #
7
- # Notes:
8
- # * Restartable jobs automatically abort if they fail. This prevents the failed job from being retried.
9
- # - To disable this behavior, add the following empty method:
10
- # def rocket_job_restart_abort
11
- # end
12
- # * On destroy this job is destroyed without starting a new instance.
13
- # * On Abort a new instance is created.
14
- # * Include `RocketJob::Plugins::Singleton` to prevent multiple copies of a job from running at
15
- # the same time.
16
- # * The job will not be restarted if:
17
- # - A validation fails after creating the new instance of this job.
18
- # - The job has expired.
19
- # * Only the fields that have `copy_on_restart: true` will be passed onto the new instance of this job.
20
- #
21
- # Example:
22
- #
23
- # class RestartableJob < RocketJob::Job
24
- # include RocketJob::Plugins::Restart
25
- #
26
- # # Retain the completed job under the completed tab in Rocket Job Web Interface.
27
- # self.destroy_on_complete = false
28
- #
29
- # # Will be copied to the new job on restart.
30
- # field :limit, type: Integer, copy_on_restart: true
31
- #
32
- # # Will _not_ be copied to the new job on restart.
33
- # field :list, type: Array, default: [1,2,3]
34
- #
35
- # # Set run_at every time a new instance of the job is created.
36
- # after_initialize set_run_at, if: :new_record?
37
- #
38
- # def perform
39
- # puts "The limit is #{limit}"
40
- # puts "The list is #{list}"
41
- # 'DONE'
42
- # end
43
- #
44
- # private
45
- #
46
- # # Run this job in 30 minutes.
47
- # def set_run_at
48
- # self.run_at = 30.minutes.from_now
49
- # end
50
- # end
51
- #
52
- # job = RestartableJob.create!(limit: 10, list: [4,5,6])
53
- # job.reload.state
54
- # # => :queued
55
- #
56
- # job.limit
57
- # # => 10
58
- #
59
- # job.list
60
- # # => [4,5,6]
61
- #
62
- # # Wait 30 minutes ...
63
- #
64
- # job.reload.state
65
- # # => :completed
66
- #
67
- # # A new instance was automatically created.
68
- # job2 = RestartableJob.last
69
- # job2.state
70
- # # => :queued
71
- #
72
- # job2.limit
73
- # # => 10
74
- #
75
- # job2.list
76
- # # => [1,2,3]
5
+ # @deprecated
77
6
  module Restart
78
7
  extend ActiveSupport::Concern
79
8
 
80
9
  included do
81
- after_abort :rocket_job_restart_new_instance
82
- after_complete :rocket_job_restart_new_instance
10
+ after_abort :create_restart!
11
+ after_complete :create_restart!
83
12
  after_fail :rocket_job_restart_abort
84
13
  end
85
14
 
86
15
  private
87
16
 
88
- # Run again in the future, even if this run fails with an exception
89
- def rocket_job_restart_new_instance
90
- if expired?
91
- logger.info("Job has expired. Not creating a new instance.")
92
- return
93
- end
94
- attributes = rocket_job_restart_attributes.each_with_object({}) { |attr, attrs| attrs[attr] = send(attr) }
95
- rocket_job_restart_create(attributes)
96
- end
97
-
98
17
  def rocket_job_restart_abort
99
18
  new_record? ? abort : abort!
100
19
  end
101
-
102
- # Allow Singleton to prevent the creation of a new job if one is already running
103
- # Retry since the delete may not have persisted to disk yet.
104
- def rocket_job_restart_create(attrs, retry_limit = 3, sleep_interval = 0.1)
105
- count = 0
106
- while count < retry_limit
107
- job = self.class.create(attrs)
108
- if job.persisted?
109
- logger.info("Created a new job instance: #{job.id}")
110
- return true
111
- else
112
- logger.info("Job already active, retrying after a short sleep")
113
- sleep(sleep_interval)
114
- end
115
- count += 1
116
- end
117
- logger.error("New job instance not started: #{job.errors.messages.inspect}")
118
- false
119
- end
120
20
  end
121
21
  end
122
22
  end