-
-
Notifications
You must be signed in to change notification settings - Fork 199
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Latency charts and histograms for individual job classes (#1411)
* Move most chart data to the ruby side Having them separated like that won't really work if charts want to do their own thing * Add a chart for total execution time to the performance index * Add a histogram for how long jobs took to execute * Make localized number helpers available to all views These are not specific to the metrics controller * Show durations between 1 and 10 seconds with one decimal place * Don't reflect the job_class query parameter directly * Fix the controller test to assert an ActiveRecord::RecordNotFound --------- Co-authored-by: Ben Sheldon [he/him] <[email protected]>
- Loading branch information
1 parent
e7cd7d7
commit 3841852
Showing
26 changed files
with
294 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
module GoodJob | ||
class BaseChart | ||
def start_end_binds | ||
end_time = Time.current | ||
start_time = end_time - 1.day | ||
|
||
[ | ||
ActiveRecord::Relation::QueryAttribute.new('start_time', start_time, ActiveRecord::Type::DateTime.new), | ||
ActiveRecord::Relation::QueryAttribute.new('end_time', end_time, ActiveRecord::Type::DateTime.new), | ||
] | ||
end | ||
|
||
def string_to_hsl(string) | ||
hash_value = string.sum | ||
|
||
hue = hash_value % 360 | ||
saturation = (hash_value % 50) + 50 | ||
lightness = '50' | ||
|
||
"hsl(#{hue}, #{saturation}%, #{lightness}%)" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# frozen_string_literal: true | ||
|
||
module GoodJob | ||
class PerformanceIndexChart < BaseChart | ||
def data | ||
table_name = GoodJob::DiscreteExecution.table_name | ||
|
||
sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish)) | ||
SELECT * | ||
FROM generate_series( | ||
date_trunc('hour', $1::timestamp), | ||
date_trunc('hour', $2::timestamp), | ||
'1 hour' | ||
) timestamp | ||
LEFT JOIN ( | ||
SELECT | ||
date_trunc('hour', scheduled_at) AS scheduled_at, | ||
job_class, | ||
SUM(duration) AS sum | ||
FROM #{table_name} sources | ||
GROUP BY date_trunc('hour', scheduled_at), job_class | ||
) sources ON sources.scheduled_at = timestamp | ||
ORDER BY timestamp ASC | ||
SQL | ||
|
||
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Chart", start_end_binds) | ||
|
||
job_names = executions_data.reject { |d| d['sum'].nil? }.map { |d| d['job_class'] || BaseFilter::EMPTY }.uniq | ||
labels = [] | ||
jobs_data = executions_data.to_a.group_by { |d| d['timestamp'] }.each_with_object({}) do |(timestamp, values), hash| | ||
labels << timestamp.in_time_zone.strftime('%H:%M') | ||
job_names.each do |job_class| | ||
sum = values.find { |d| d['job_class'] == job_class }&.[]('sum') | ||
duration = sum ? ActiveSupport::Duration.parse(sum).to_f : 0 | ||
(hash[job_class] ||= []) << duration | ||
end | ||
end | ||
|
||
{ | ||
type: "line", | ||
data: { | ||
labels: labels, | ||
datasets: jobs_data.map do |job_class, data| | ||
label = job_class || '(none)' | ||
{ | ||
label: label, | ||
data: data, | ||
backgroundColor: string_to_hsl(label), | ||
borderColor: string_to_hsl(label), | ||
} | ||
end, | ||
}, | ||
options: { | ||
plugins: { | ||
title: { | ||
display: true, | ||
text: I18n.t("good_job.performance.index.chart_title"), | ||
}, | ||
}, | ||
scales: { | ||
y: { | ||
beginAtZero: true, | ||
}, | ||
}, | ||
}, | ||
} | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# frozen_string_literal: true | ||
|
||
module GoodJob | ||
class PerformanceShowChart < BaseChart | ||
# These numbers are lifted from Sidekiq | ||
BUCKET_INTERVALS = [ | ||
0.02, 0.03, 0.045, 0.065, 0.1, | ||
0.15, 0.225, 0.335, 0.5, 0.75, | ||
1.1, 1.7, 2.5, 3.8, 5.75, | ||
8.5, 13, 20, 30, 45, | ||
65, 100, 150, 225, 335, | ||
10**8 # About 3 years | ||
].freeze | ||
|
||
def initialize(job_class) | ||
super() | ||
@job_class = job_class | ||
end | ||
|
||
def data | ||
table_name = GoodJob::DiscreteExecution.table_name | ||
|
||
interval_entries = BUCKET_INTERVALS.map { "interval '#{_1}'" }.join(",") | ||
sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish)) | ||
SELECT | ||
WIDTH_BUCKET(duration, ARRAY[#{interval_entries}]) as bucket_index, | ||
COUNT(WIDTH_BUCKET(duration, ARRAY[#{interval_entries}])) AS count | ||
FROM #{table_name} sources | ||
WHERE | ||
scheduled_at > $1::timestamp | ||
AND scheduled_at < $2::timestamp | ||
AND job_class = $3 | ||
AND duration IS NOT NULL | ||
GROUP BY bucket_index | ||
SQL | ||
|
||
binds = [ | ||
*start_end_binds, | ||
@job_class, | ||
] | ||
labels = BUCKET_INTERVALS.map { |interval| GoodJob::ApplicationController.helpers.format_duration(interval) } | ||
labels[-1] = I18n.t("good_job.performance.show.slow") | ||
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Job Chart", binds) | ||
executions_data = executions_data.to_a.index_by { |data| data["bucket_index"] } | ||
|
||
bucket_data = 0.upto(BUCKET_INTERVALS.count).map do |bucket_index| | ||
executions_data.dig(bucket_index, "count") || 0 | ||
end | ||
|
||
{ | ||
type: "bar", | ||
data: { | ||
labels: labels, | ||
datasets: [{ | ||
label: @job_class, | ||
data: bucket_data, | ||
backgroundColor: string_to_hsl(@job_class), | ||
borderColor: string_to_hsl(@job_class), | ||
}], | ||
}, | ||
options: { | ||
scales: { | ||
y: { | ||
beginAtZero: true, | ||
}, | ||
}, | ||
}, | ||
} | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<div class="border-bottom"> | ||
<h2 class="pt-3 pb-2"><%= t ".title" %> - <%= @job_class %></h2> | ||
</div> | ||
|
||
<%= render 'good_job/shared/chart', chart_data: GoodJob::PerformanceShowChart.new(@job_class).data %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.