app/models/naf/application_schedule.rb in naf-2.0.4 vs app/models/naf/application_schedule.rb in naf-2.1.0

- old
+ new

@@ -8,14 +8,18 @@ :application_run_group_name, :run_interval, :priority, :visible, :enabled, - :run_start_minute, :application_run_group_limit, + :application_run_group_quantum, :application_schedule_prerequisites_attributes, - :enqueue_backlogs + :enqueue_backlogs, + :run_interval_style_id, + :application, + :run_interval_style, + :application_run_group_restriction SCHEDULES_LOCK_ID = 0 #--------------------- # *** Associations *** @@ -23,10 +27,12 @@ belongs_to :application, class_name: '::Naf::Application' belongs_to :application_run_group_restriction, class_name: '::Naf::ApplicationRunGroupRestriction' + belongs_to :run_interval_style, + class_name: '::Naf::RunIntervalStyle' has_many :application_schedule_affinity_tabs, class_name: '::Naf::ApplicationScheduleAffinityTab', dependent: :destroy has_many :affinities, through: :application_schedule_affinity_tabs @@ -42,11 +48,14 @@ #-------------------- # *** Validations *** #++++++++++++++++++++ - validates :application_run_group_restriction_id, presence: true + validates :application_run_group_restriction_id, + :run_interval_style_id, + :application_id, + :priority, presence: true validates :priority, numericality: { only_integer: true, greater_than: -2147483648, less_than: 2147483647 } @@ -54,28 +63,21 @@ only_integer: true, greater_than_or_equal_to: 1, less_than: 2147483647, allow_blank: true } - validates :run_start_minute, numericality: { - only_integer: true, - greater_than_or_equal_to: 0, - less_than: 24*60, - allow_blank: true - } validates :run_interval, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 2147483647, allow_blank: true } before_save :check_blank_values validate :visible_enabled_check - validate :run_interval_at_time_check - validate :enabled_application_id_unique validate :prerequisite_application_schedule_id_uniqueness + validate :run_interval_check #-------------------- # *** Delegations *** #++++++++++++++++++++ @@ -92,21 +94,106 @@ def self.unlock_schedules unlock_record(SCHEDULES_LOCK_ID) end - def self.exact_schedules - where('run_start_minute is not null') + # find the exact based schedules that should be queued + # select anything that + # isn't currently running (or queued) AND + # hasn't run since run_interval AND + # should have been run by now + def self.exact_schedules(time, not_finished_applications, application_last_runs) + custom_current_time = time.to_date + time.strftime('%H').to_i.hours + time.strftime('%M').to_i.minutes + schedules = ::Naf::ApplicationSchedule. + joins(:run_interval_style). + where("#{Naf.schema_name}.run_interval_styles.name IN (?)", ['at beginning of day', 'at beginning of hour']). + enabled.select do |schedule| + + interval_time = time.to_date + if schedule.run_interval_style.name == 'at beginning of day' + interval_time += schedule.run_interval.minutes + elsif schedule.run_interval_style.name == 'at beginning of hour' + interval_time += time.strftime('%H').to_i.hours + schedule.run_interval.minutes + end + + (not_finished_applications[schedule.id].nil? && + (application_last_runs[schedule.id].nil? || + (interval_time >= application_last_runs[schedule.id].finished_at) + ) && + (custom_current_time - interval_time) == 0.seconds + ) + end + + schedules end - def self.relative_schedules - where('run_interval >= 0') + # find the interval based schedules that should be queued + # select anything that isn't currently running and completed + # running more than run_interval minutes ago + def self.relative_schedules(time, not_finished_applications, application_last_runs) + schedules = ::Naf::ApplicationSchedule. + joins(:run_interval_style). + where("#{Naf.schema_name}.run_interval_styles.name = ?", 'after previous run'). + enabled.select do |schedule| + + (not_finished_applications[schedule.id].nil? && + (application_last_runs[schedule.id].nil? || + (time - application_last_runs[schedule.id].finished_at) > schedule.run_interval.minutes + ) + ) + end + + schedules end - def self.pickables + def self.constant_schedules + ::Naf::ApplicationSchedule. + joins(:run_interval_style). + where("#{Naf.schema_name}.run_interval_styles.name = ?", 'keep running'). + enabled + end + + def self.enabled + where(enabled: true) + end + + def self.should_be_queued + current_time = Time.zone.now + # Applications that are still running + not_finished_applications = ::Naf::HistoricalJob. + queued_between(current_time - Naf::HistoricalJob::JOB_STALE_TIME, current_time). + where("finished_at IS NULL AND request_to_terminate = false"). + find_all{ |job| job.application_schedule_id.present? }. + index_by{ |job| job.application_schedule_id } + + # Last ran job for each application + application_last_runs = ::Naf::HistoricalJob.application_last_runs. + index_by{ |job| job.application_schedule_id } + + relative_schedules = ::Naf::ApplicationSchedule. + relative_schedules(current_time, not_finished_applications, application_last_runs) + exact_schedules = ::Naf::ApplicationSchedule. + exact_schedules(current_time, not_finished_applications, application_last_runs) + constant_schedules = ::Naf::ApplicationSchedule.constant_schedules + + foreman = ::Logical::Naf::ConstructionZone::Foreman.new + return (relative_schedules + exact_schedules + constant_schedules).select do |schedule| + affinities = [] + schedule.affinities.each do |affinity| + affinities << { affinity_id: affinity.id } + end + + schedule.enqueue_backlogs || !foreman.limited_by_run_group?(schedule.application_run_group_restriction, + schedule.application_run_group_name, + schedule.application_run_group_limit, + affinities) + end + end + + def self.pickleables # check the application is deleted - self.where(:deleted => false) + self.where(deleted: false) end #------------------------- # *** Instance Methods *** #+++++++++++++++++++++++++ @@ -122,15 +209,11 @@ components << "HIDDEN|DISABLED" end end components << "id: #{id}" components << "\"#{application.title}\"" - if run_start_minute - components << "start at: #{"%02d" % (run_start_minute/60)}:#{"%02d" % (run_start_minute%60)}" - else - components << "start every: #{run_interval} minutes" - end + components << ::Logical::Naf::ApplicationSchedule.new(self).display return "::Naf::ApplicationSchedule<#{components.join(', ')}>" end def visible_enabled_check @@ -138,32 +221,23 @@ errors.add(:visible, "must be true, or set enabled to false") errors.add(:enabled, "must be false, if visible is set to false") end end - def enabled_application_id_unique - return unless enabled - - if id - conditions = ["id <> ? AND application_id = ? AND enabled = ?", id, application_id, true] + # When rolling back from Naf v2.1 to v2.0, check whether run_interval + # or run_start_minute is nil. Otherwise, just check the presence of + # run_interval. + def run_interval_check + if self.attributes.keys.include?('run_start_minute') + if !run_start_minute.present? && !run_interval.present? + errors.add(:run_interval, "or run_start_minute must be nil") + errors.add(:run_start_minute, "or run_interval must be nil") + end else - conditions = ["application_id = ? AND enabled = ?", application_id, true] + if !run_interval.present? + errors.add(:run_interval, "must be present") + end end - - num_collisions = self.class.count(conditions: conditions) - errors.add(:application_id, "is enabled and has already been taken") if num_collisions > 0 - end - - def run_interval_at_time_check - unless (run_start_minute.blank? || run_interval.blank?) - errors.add(:run_interval, "or Run start minute must be nil") - errors.add(:run_start_minute, "or Run interval must be nil") - end - end - - def self.pickleables(pickler) - self.joins(:application). - where("#{::Naf.schema_name}.applications.deleted IS FALSE") end private def prerequisite_application_schedule_id_uniqueness