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