class Bookie::Database::JobSummary
A cached summary of Jobs in the database
Most summary operations should be performed through this class to improve efficiency.
Public Class Methods
Filters by command name
# File lib/bookie/database/job_summary.rb, line 85 def self.by_command_name(cmd) where('job_summaries.command_name = ?', cmd) end
Filters by date
# File lib/bookie/database/job_summary.rb, line 21 def self.by_date(date) where('job_summaries.date = ?', date) end
Filters by a date range
# File lib/bookie/database/job_summary.rb, line 27 def self.by_date_range(range) range = range.normalized if range.exclude_end? where('? <= job_summaries.date AND job_summaries.date < ?', range.begin, range.end) else where('? <= job_summaries.date AND job_summaries.date <= ?', range.begin, range.end) end end
Filters by group
# File lib/bookie/database/job_summary.rb, line 50 def self.by_group(group) joins(:user).where('users.group_id = ?', group.id) end
Filters by group name
# File lib/bookie/database/job_summary.rb, line 56 def self.by_group_name(name) group = Group.where(:name => name).first if group by_group(group) else self.none end end
Filters by system
# File lib/bookie/database/job_summary.rb, line 67 def self.by_system(system) where('job_summaries.system_id = ?', system.id) end
Filters by system name
# File lib/bookie/database/job_summary.rb, line 73 def self.by_system_name(name) joins(:system).where('systems.name = ?', name) end
Filters by system type
# File lib/bookie/database/job_summary.rb, line 79 def self.by_system_type(type) joins(:system).where('systems.system_type_id = ?', type.id) end
Filters by user
# File lib/bookie/database/job_summary.rb, line 38 def self.by_user(user) where('job_summaries.user_id = ?', user.id) end
Filters by user name
# File lib/bookie/database/job_summary.rb, line 44 def self.by_user_name(name) joins(:user).where('users.name = ?', name) end
Create cached summaries for the given date
The date is interpreted as being in UTC.
If there is nothing to summarize, a dummy summary will be created.
Uses Lock::synchronize internally; should not be used in transaction blocks
# File lib/bookie/database/job_summary.rb, line 97 def self.summarize(date) jobs = Job unscoped = self.unscoped time_min = date.to_utc_time time_range = time_min ... time_min + 1.days day_jobs = jobs.by_time_range(time_range) #Find the unique combinations of values for some of the jobs' attributes. value_sets = day_jobs.uniq.pluck(:user_id, :system_id, :command_name) if value_sets.empty? #There are no jobs, so create a dummy summary. user = User.select(:id).first system = System.select(:id).first #If there are no users or no systems, we can't create the dummy summary, so just return. #To consider: figure out how to create the dummy summary anyway? return unless user && system #Create a dummy summary so summary() doesn't keep trying to create one. Lock[:job_summaries].synchronize do sum = unscoped.find_or_initialize_by( :date => date, :user_id => user.id, :system_id => system.id, :command_name => '' ) sum.cpu_time = 0 sum.memory_time = 0 sum.save! end else value_sets.each do |set| summary_jobs = jobs.where( :user_id => set[0], :system_id => set[1], :command_name => set[2] ) summary = summary_jobs.summary(time_range) Lock[:job_summaries].synchronize do sum = unscoped.find_or_initialize_by( :date => date, :user_id => set[0], :system_id => set[1], :command_name => set[2] ) sum.cpu_time = summary[:cpu_time] sum.memory_time = summary[:memory_time] sum.save! end end end end
Returns a summary of jobs in the database
The following options are supported:
:range
-
restricts the summary to a specific time interval (specified as a
Range
of Time objects)
:jobs
-
the jobs on which the summary should operate
Internally, this may call JobSummary::summarize
, which uses Lock#synchronize
, so this should not be used inside a transaction block.
When filtering, the same filters must be applied to both the Jobs and the JobSummaries. For example:
jobs = Bookie::Database::Job.by_user_name('root') summaries = Bookie::Database::Job.by_user_name('root') puts summaries.summary(:jobs => jobs) TODO: test that summaries are created on UTC date boundaries?
# File lib/bookie/database/job_summary.rb, line 163 def self.summary(opts = {}) jobs = opts[:jobs] || Job time_range = opts[:range] unless time_range start_time = jobs.minimum(:start_time) end_time = jobs.maximum(:end_time) if start_time && end_time time_range = start_time .. end_time else time_range = Time.new ... Time.new end end time_range = time_range.normalized date_begin = time_range.begin.utc.to_date rounded_date_begin = false #Round date_begin up. if date_begin.to_utc_time < time_range.begin date_begin += 1 rounded_date_begin = true end date_end = time_range.end.utc.to_date #Is the interval large enough to cover any cached summaries? if date_begin >= date_end #Nope; just return a regular summary. return jobs.summary(time_range) end jobs_in_range = jobs.by_time_range(time_range) num_jobs = jobs_in_range.count successful = jobs_in_range.where('jobs.exit_code = 0').count cpu_time = 0 memory_time = 0 #To consider: check if num_jobs is zero so we can skip all this? if rounded_date_begin #We need to get a summary for the chunk up to the first whole day. summary = jobs.summary(time_range.begin ... date_begin.to_utc_time) cpu_time += summary[:cpu_time] memory_time += summary[:memory_time] end date_end_time = date_end.to_utc_time if time_range.cover?(date_end_time) #We need to get a summary for the chunk after the last whole day. range = Range.new(date_end_time, time_range.end, time_range.exclude_end?) summary = jobs.summary(range) cpu_time += summary[:cpu_time] memory_time += summary[:memory_time] end date_range = date_begin ... date_end #Now we can process the cached summaries. unscoped = self.unscoped summaries = by_date_range(date_range).order(:date).to_a index = 0 date_range.each do |date| new_index = index summary = summaries[new_index] while summary && summary.date == date do cpu_time += summary.cpu_time memory_time += summary.memory_time new_index += 1 summary = summaries[new_index] end #Did we actually process any summaries? #If not, have _any_ summaries been created for this day? if new_index == index && !(unscoped.by_date(date).any?) #Nope. Create the summaries. unscoped.summarize(date) #TODO: what if a Sender deletes the summaries right before this? self.by_date(date).each do |sum| cpu_time += sum.cpu_time memory_time += sum.memory_time end end index = new_index end { :num_jobs => num_jobs, :successful => successful, :cpu_time => cpu_time, :memory_time => memory_time, } end