lib/rocket_job/plugins/cron.rb in rocketjob-6.0.0.rc3 vs lib/rocket_job/plugins/cron.rb in rocketjob-6.0.0

- old
+ new

@@ -12,44 +12,84 @@ # by clicking `Run Now` in the web interface. module Cron extend ActiveSupport::Concern included do - include Restart - field :cron_schedule, type: String, class_attribute: true, user_editable: true, copy_on_restart: true + # Whether to prevent another instance of this job from running with the exact _same_ cron schedule. + # Another job instance with a different `cron_schedule` string is permitted. + field :cron_singleton, type: Mongoid::Boolean, default: true, class_attribute: true, user_editable: true, copy_on_restart: true + + # Whether to re-schedule the next job occurrence when this job starts, or when it is complete. + # + # `true`: Create a new scheduled instance of this job after it has started. (Default) + # - Ensures that the next scheduled instance is not missed because the current instance is still running. + # - Any changes to fields marked with `copy_on_restart` of `true` will be saved to the new scheduled instance + # _only_ if they were changed during an `after_start` callback. + # Changes to these during other callbacks or during the `perform` will not be saved to the new scheduled + # instance. + # - To prevent this job creating any new duplicate instances during subsequent processing, + # its `cron_schedule` is set to `nil`. + # + # `false`: Create a new scheduled instance of this job on `fail`, or `abort`. + # - Prevents the next scheduled instance from running or being scheduled while the current instance is + # still running. + # - Any changes to fields marked with `copy_on_restart` of `true` will be saved to the new scheduled instance + # at any time until after the job has failed, or is aborted. + # - To prevent this job creating any new duplicate instances during subsequent processing, + # its `cron_schedule` is set to `nil` after it fails or is aborted. + field :cron_after_start, type: Mongoid::Boolean, default: true, class_attribute: true, user_editable: true, copy_on_restart: true + validates_each :cron_schedule do |record, attr, value| record.errors.add(attr, "Invalid cron_schedule: #{value.inspect}") if value && !Fugit::Cron.new(value) end + validate :rocket_job_cron_singleton_check + before_save :rocket_job_cron_set_run_at - private + after_start :rocket_job_cron_on_start + after_abort :rocket_job_cron_end_state + after_complete :rocket_job_cron_end_state + after_fail :rocket_job_cron_end_state + end - # Prevent auto restart if this job does not have a cron schedule. - # Overrides: RocketJob::Plugins::Restart#rocket_job_restart_new_instance - def rocket_job_restart_new_instance - return unless cron_schedule + def rocket_job_cron_set_run_at + return if cron_schedule.nil? || !(cron_schedule_changed? && !run_at_changed?) - super - end + self.run_at = Fugit::Cron.new(cron_schedule).next_time.to_utc_time + end - # On failure: - # - create a new instance scheduled to run in the future. - # - clear out the `cron_schedule` so this instance will not schedule another instance to run on completion. - # Overrides: RocketJob::Plugins::Restart#rocket_job_restart_abort - def rocket_job_restart_abort - return unless cron_schedule + private - rocket_job_restart_new_instance - update_attribute(:cron_schedule, nil) + def rocket_job_cron_on_start + return unless cron_schedule && cron_after_start + + current_cron_schedule = cron_schedule + update_attribute(:cron_schedule, nil) + create_restart!(cron_schedule: current_cron_schedule) + end + + def rocket_job_cron_end_state + return unless cron_schedule && !cron_after_start + + current_cron_schedule = cron_schedule + update_attribute(:cron_schedule, nil) + create_restart!(cron_schedule: current_cron_schedule) + end + + # Returns [true|false] whether another instance of this job with the same cron schedule is already active + def rocket_job_cron_duplicate? + self.class.with(read: {mode: :primary}) do |conn| + conn.where(:state.in => %i[queued running failed paused], :id.ne => id, cron_schedule: cron_schedule).exists? end end - def rocket_job_cron_set_run_at - return if cron_schedule.nil? || !(cron_schedule_changed? && !run_at_changed?) + # Prevent creation of a new job when another is running with the same cron schedule. + def rocket_job_cron_singleton_check + return if cron_schedule.nil? || completed? || aborted? || !rocket_job_cron_duplicate? - self.run_at = Fugit::Cron.new(cron_schedule).next_time.to_utc_time + errors.add(:state, "Another instance of #{self.class.name} is already queued, running, failed, or paused with the same cron schedule: #{cron_schedule}") end end end end