module CronoTrigger::Schedulable
Constants
- DEFAULT_EXECUTE_LOCK_TIMEOUT
- DEFAULT_RETRY_INTERVAL
- DEFAULT_RETRY_LIMIT
Public Class Methods
included_by()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 19 def self.included_by @included_by end
Public Instance Methods
abort_execution!()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 219 def abort_execution! reset!(false) end
activate_schedule!(at: Time.current)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 143 def activate_schedule!(at: Time.current) time = calculate_next_execute_at || at attributes = {} unless self[crono_trigger_column_name(:next_execute_at)] attributes[crono_trigger_column_name(:next_execute_at)] = time end if self.class.column_names.include?(crono_trigger_column_name(:started_at)) unless self[crono_trigger_column_name(:started_at)] attributes[crono_trigger_column_name(:started_at)] = time end end if new_record? self.attributes = attributes else merge_updated_at_for_crono_trigger!(attributes) update_columns(attributes) end self end
assume_executing?()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 269 def assume_executing? locking? end
crono_trigger_column_name(name)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 273 def crono_trigger_column_name(name) self.class.crono_trigger_column_name(name) end
crono_trigger_lock!(**attributes)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 223 def crono_trigger_lock!(**attributes) attributes = { crono_trigger_column_name(:execute_lock) => Time.current.to_i, crono_trigger_column_name(:locked_by) => CronoTrigger.config.worker_id }.merge(attributes) merge_updated_at_for_crono_trigger!(attributes) if new_record? self.attributes = attributes else update_columns(attributes) end end
crono_trigger_status()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 245 def crono_trigger_status case when locking? :locked when waiting? :waiting when not_scheduled? :not_scheduled end end
crono_trigger_unlock!()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 236 def crono_trigger_unlock! attributes = { crono_trigger_column_name(:execute_lock) => 0, crono_trigger_column_name(:locked_by) => nil, } merge_updated_at_for_crono_trigger!(attributes) update_columns(attributes) end
do_execute()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 112 def do_execute ExecutionTracker.track(self) do do_execute_with_catch end rescue Exception => ex logger.error(ex) if logger save_last_error_info(ex) retry_or_reset!(ex) end
execute_now()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 277 def execute_now crono_trigger_lock!(next_execute_at: Time.now) save! if new_record? do_execute end
locking?(at: Time.now)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 264 def locking?(at: Time.now) self[crono_trigger_column_name(:execute_lock)] > 0 && self[crono_trigger_column_name(:execute_lock)] >= at.to_f - self.class.execute_lock_timeout end
not_scheduled?()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 260 def not_scheduled? self[crono_trigger_column_name(:next_execute_at)].nil? && last_executed_at.nil? end
reset!(update_last_executed_at = true)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 192 def reset!(update_last_executed_at = true) logger.info "Reset execution schedule #{self.class}-#{id}" if logger attributes = { crono_trigger_column_name(:next_execute_at) => calculate_next_execute_at, crono_trigger_column_name(:execute_lock) => 0, crono_trigger_column_name(:locked_by) => nil, } now = Time.current if update_last_executed_at && self.class.column_names.include?(crono_trigger_column_name(:last_executed_at)) attributes.merge!(crono_trigger_column_name(:last_executed_at) => now) end if self.class.column_names.include?("retry_count") attributes.merge!(retry_count: 0) end if self.class.column_names.include?(crono_trigger_column_name(:current_cycle_id)) attributes.merge!(crono_trigger_column_name(:current_cycle_id) => SecureRandom.uuid) end merge_updated_at_for_crono_trigger!(attributes, now) update_columns(attributes) end
retry!(immediately: false)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 167 def retry!(immediately: false) run_callbacks :retry do logger.info "Retry #{self.class}-#{id}" if logger now = Time.current if immediately wait = 0 else wait = crono_trigger_options[:exponential_backoff] ? retry_interval * [2 * (retry_count - 1), 1].max : retry_interval end attributes = { crono_trigger_column_name(:next_execute_at) => now + wait, crono_trigger_column_name(:execute_lock) => 0, crono_trigger_column_name(:locked_by) => nil, } if self.class.column_names.include?("retry_count") attributes.merge!(retry_count: retry_count.to_i + 1) end merge_updated_at_for_crono_trigger!(attributes, now) update_columns(attributes) end end
waiting?()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 256 def waiting? !!self[crono_trigger_column_name(:next_execute_at)] end
Private Instance Methods
calculate_next_execute_at(now = Time.current)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 294 def calculate_next_execute_at(now = Time.current) if self[crono_trigger_column_name(:cron)] tz = self[crono_trigger_column_name(:timezone)].try { |zn| TZInfo::Timezone.get(zn) } base = [now, self[crono_trigger_column_name(:started_at)]].compact.max cron_now = tz ? base.in_time_zone(tz) : base calculated = Chrono::NextTime.new(now: cron_now, source: self[crono_trigger_column_name(:cron)]).to_time return calculated unless self[crono_trigger_column_name(:finished_at)] return if calculated > self[crono_trigger_column_name(:finished_at)] calculated end end
do_execute_with_catch()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 122 def do_execute_with_catch catch(:ok_without_reset) do catch(:ok) do catch(:retry) do catch(:abort) do run_callbacks :execute do execute end throw :ok end abort_execution! return :abort end retry! return :retry end reset! return :ok end end
merge_updated_at_for_crono_trigger!(attributes, time = Time.current)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 363 def merge_updated_at_for_crono_trigger!(attributes, time = Time.current) if self.class.column_names.include?("updated_at") attributes.merge!("updated_at" => time) end end
retry_interval()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 338 def retry_interval crono_trigger_options[:retry_interval] || DEFAULT_RETRY_INTERVAL end
retry_limit()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 334 def retry_limit crono_trigger_options[:retry_limit] || DEFAULT_RETRY_LIMIT end
retry_or_reset!(ex)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 285 def retry_or_reset!(ex) if respond_to?(:retry_count) && retry_count.to_i < retry_limit retry! else CronoTrigger::ExceptionHandler.handle_exception(self, ex) reset!(false) end end
save_last_error_info(ex)
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 342 def save_last_error_info(ex) columns = self.class.column_names attributes = {} now = Time.current if columns.include?("last_error_name") attributes.merge!(last_error_name: ex.class.to_s) end if columns.include?("last_error_reason") attributes.merge!(last_error_reason: ex.message) end if columns.include?("last_error_time") attributes.merge!(last_error_time: now) end merge_updated_at_for_crono_trigger!(attributes) update_columns(attributes) unless attributes.empty? end
set_current_cycle_id()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 308 def set_current_cycle_id if self.class.column_names.include?(crono_trigger_column_name(:current_cycle_id)) && self[crono_trigger_column_name(:current_cycle_id)].nil? self[crono_trigger_column_name(:current_cycle_id)] = SecureRandom.uuid end end
update_next_execute_at_if_update_cron()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 315 def update_next_execute_at_if_update_cron if changes[crono_trigger_column_name(:cron)] || changes[crono_trigger_column_name(:timezone)] if self[crono_trigger_column_name(:cron)] self[crono_trigger_column_name(:next_execute_at)] = calculate_next_execute_at end end end
validate_cron_format()
click to toggle source
# File lib/crono_trigger/schedulable.rb, line 323 def validate_cron_format return unless self[crono_trigger_column_name(:cron)] Chrono::NextTime.new(now: Time.current, source: self[crono_trigger_column_name(:cron)]).to_time rescue Chrono::Fields::Base::InvalidField self.errors.add( crono_trigger_column_name(:cron).to_sym, crono_trigger_options["invalid_field_error_message"] || "has invalid field" ) end