Skip to content

Commit 98cd0f8

Browse files
committed
add performance data to monitor status page
* save search query & term fetch performance data to database for every request * calculate averages for load, normalization, and combined times * calculate averages for last 24 hours, last 30 days, and last 12 months * ignore dynamically created graphs Currently splits on time periods: day, month, year Might also want to split on: * action: fetch, search * per authority
1 parent e6a7e65 commit 98cd0f8

File tree

11 files changed

+433
-12
lines changed

11 files changed

+433
-12
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ script/log
3838
/public/documents
3939
/public/assets
4040
/public/sitemap.xml.gz
41+
/app/assets/images/qa_server/charts/*
4142

4243
# ignore renderer file
4344
# shows the template being rendered in the html source

app/assets/stylesheets/qa_server/_monitor-status.scss

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,37 @@ p.status-update-dtstamp {
1111
color: darkred;
1212
font-style: italic;
1313
}
14+
15+
div.right-menu-section {
16+
padding-top: 50px;
17+
}
18+
19+
ul.right-menu {
20+
list-style-type: none;
21+
li.clickable {
22+
display: inline;
23+
padding-right: 30px;
24+
color: #337ab7;
25+
}
26+
li.clickable:hover {
27+
color: #23527c;
28+
text-decoration: underline;
29+
}
30+
li.selected {
31+
display: inline;
32+
padding-right: 30px;
33+
font-weight: bold;
34+
}
35+
}
36+
37+
div#performance-by-the-hour {
38+
display: block;
39+
}
40+
41+
div#performance-by-the-day {
42+
display: none;
43+
}
44+
45+
div#performance-by-the-month {
46+
display: none;
47+
}

app/controllers/qa_server/monitor_status_controller.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ module QaServer
44
class MonitorStatusController < QaServer::AuthorityValidationController
55
class_attribute :presenter_class,
66
:scenario_run_registry_class,
7-
:scenario_history_class
7+
:scenario_history_class,
8+
:performance_history_class
89

910
self.presenter_class = QaServer::MonitorStatusPresenter
1011
self.scenario_run_registry_class = QaServer::ScenarioRunRegistry
1112
self.scenario_history_class = QaServer::ScenarioRunHistory
13+
self.performance_history_class = QaServer::PerformanceHistory
1214

1315
# Sets up presenter with data to display in the UI
1416
def index
@@ -20,7 +22,7 @@ def index
2022
@presenter = presenter_class.new(current_summary: latest_summary,
2123
current_failure_data: latest_failures,
2224
historical_summary_data: historical_summary_data,
23-
performance_data: [])
25+
performance_data: performance_history_class.performance_data)
2426
render 'index', status: :internal_server_error if latest_summary.failing_authority_count.positive?
2527
end
2628

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# frozen_string_literal: true
2+
# Provide access to the scenario_results_history database table which tracks specific scenario runs over time.
3+
module QaServer
4+
class PerformanceHistory < ActiveRecord::Base
5+
self.table_name = 'performance_history'
6+
7+
enum action: [:fetch, :search]
8+
9+
PERFORMANCE_FOR_DAY_KEY = :day
10+
PERFORMANCE_BY_HOUR_KEY = :hour
11+
12+
PERFORMANCE_FOR_MONTH_KEY = :month
13+
PERFORMANCE_BY_DAY_KEY = :day
14+
15+
PERFORMANCE_FOR_YEAR_KEY = :year
16+
PERFORMANCE_BY_MONTH_KEY = :month
17+
18+
LOAD_TIME_KEY = :load_avg_ms
19+
NORMALIZATION_TIME_KEY = :normalization_avg_ms
20+
COMBINED_TIME_KEY = :combined_avg_ms
21+
22+
class << self
23+
24+
# Save a scenario result
25+
# @param run_id [Integer] the run on which to gather statistics
26+
# @param result [Hash] the scenario result to be saved
27+
def save_result(dt_stamp:, authority:, action:, size_bytes:, load_time_ms:, normalization_time_ms: )
28+
QaServer::PerformanceHistory.create(dt_stamp: dt_stamp,
29+
authority: authority,
30+
action: action,
31+
size_bytes: size_bytes,
32+
load_time_ms: load_time_ms,
33+
normalization_time_ms: normalization_time_ms)
34+
end
35+
36+
# Performance data for a day, a month, and a year.
37+
# @returns [Hash] performance statistics for the past 24 hours
38+
# @example
39+
# { 0: { hour: 1400, load_avg_ms: 12.3, normalization_avg_ms: 4.2 },
40+
# 1: { hour: 1500, load_avg_ms: 12.3, normalization_avg_ms: 4.2 },
41+
# 2: { hour: 1600, load_avg_ms: 12.3, normalization_avg_ms: 4.2 },
42+
# ...,
43+
# 23: { hour: 1300, load_avg_ms: 12.3, normalization_avg_ms: 4.2 }
44+
# }
45+
def performance_data
46+
data = {}
47+
data[PERFORMANCE_FOR_DAY_KEY] = average_last_24_hours
48+
data[PERFORMANCE_FOR_MONTH_KEY] = average_last_30_days
49+
data[PERFORMANCE_FOR_YEAR_KEY] = average_last_12_months
50+
data
51+
end
52+
53+
private
54+
55+
# Get hourly average for the past 24 hours.
56+
# @returns [Hash] performance statistics for the past 24 hours
57+
# @example
58+
# { 0: { hour: 1400, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
59+
# 1: { hour: 1500, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
60+
# 2: { hour: 1600, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
61+
# ...,
62+
# 23: { hour: 1300, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 }
63+
# }
64+
def average_last_24_hours
65+
start_hour = Time.now.beginning_of_hour - 23.hour
66+
avgs = {}
67+
0.upto(23).each do |idx|
68+
records = PerformanceHistory.where(dt_stamp: start_hour..start_hour.end_of_hour)
69+
averages = calculate_averages(records)
70+
data = {}
71+
data[PERFORMANCE_BY_HOUR_KEY] = idx == 23 ? I18n.t('qa_server.monitor_status.performance.now') : ((idx + 1) % 2 == 0 ? (start_hour.hour * 100).to_s : "")
72+
data[LOAD_TIME_KEY] = averages[:avg_load_time_ms]
73+
data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
74+
data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
75+
avgs[idx] = data
76+
start_hour = start_hour + 1.hour
77+
end
78+
avgs
79+
end
80+
81+
# Get daily average for the past 30 days.
82+
# @returns [Hash] performance statistics for the past 30 days
83+
# @example
84+
# { 0: { day: '07-15-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
85+
# 1: { day: '07-16-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
86+
# 2: { day: '07-17-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
87+
# ...,
88+
# 29: { day: '08-13-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 }
89+
# }
90+
def average_last_30_days
91+
start_day = Time.now.beginning_of_day - 29.day
92+
avgs = {}
93+
0.upto(29).each do |idx|
94+
records = PerformanceHistory.where(dt_stamp: start_day..start_day.end_of_day)
95+
averages = calculate_averages(records)
96+
data = {}
97+
data[PERFORMANCE_BY_DAY_KEY] = idx == 29 ? I18n.t('qa_server.monitor_status.performance.today') : ((idx + 1) % 5 == 0 ? (start_day).strftime("%m-%d") : "")
98+
data[LOAD_TIME_KEY] = averages[:avg_load_time_ms]
99+
data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
100+
data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
101+
avgs[idx] = data
102+
start_day = start_day + 1.day
103+
end
104+
avgs
105+
end
106+
107+
# Get daily average for the past 12 months.
108+
# @returns [Hash] performance statistics for the past 12 months
109+
# @example
110+
# { 0: { month: '09-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
111+
# 1: { month: '10-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
112+
# 2: { month: '11-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
113+
# ...,
114+
# 11: { month: '08-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 }
115+
# }
116+
def average_last_12_months
117+
start_month = Time.now.beginning_of_month - 11.month
118+
avgs = {}
119+
0.upto(11).each do |idx|
120+
records = PerformanceHistory.where(dt_stamp: start_month..start_month.end_of_month)
121+
averages = calculate_averages(records)
122+
data = {}
123+
data[PERFORMANCE_BY_MONTH_KEY] = (start_month).strftime("%m-%Y")
124+
data[LOAD_TIME_KEY] = averages[:avg_load_time_ms]
125+
data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
126+
data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
127+
avgs[idx] = data
128+
start_month = start_month + 1.month
129+
end
130+
avgs
131+
end
132+
133+
def calculate_averages(records)
134+
return { avg_load_time_ms: 0, avg_normalization_time_ms: 0, avg_combined_time_ms: 0 } if records.count.zero?
135+
sum_load_times = 0
136+
sum_normalization_times = 0
137+
sum_combined_times = 0
138+
records.each do |record|
139+
sum_load_times += record.load_time_ms
140+
sum_normalization_times += record.normalization_time_ms
141+
sum_combined_times += (record.load_time_ms + record.normalization_time_ms)
142+
end
143+
{
144+
avg_load_time_ms: sum_load_times / records.count,
145+
avg_normalization_time_ms: sum_normalization_times / records.count,
146+
avg_combined_time_ms: sum_combined_times / records.count
147+
}
148+
end
149+
end
150+
end
151+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module PrependedLinkedData::FindTerm
2+
# Override Qa::Authorities::LinkedData::FindTerm#find method
3+
# @return [Hash] single term results in requested format
4+
def find(id, language: nil, replacements: {}, subauth: nil, format: nil, jsonld: false, performance_data: false) # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
5+
saved_performance_data = performance_data
6+
performance_data = true
7+
full_results = super
8+
QaServer::PerformanceHistory.save_result(dt_stamp: Time.now,
9+
authority: authority_name,
10+
action: 'fetch',
11+
size_bytes: full_results[:performance][:fetched_bytes],
12+
load_time_ms: (full_results[:performance][:fetch_time_s] * 1000),
13+
normalization_time_ms: (full_results[:performance][:normalization_time_s] * 1000))
14+
saved_performance_data ? full_results : full_results[:results]
15+
end
16+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module PrependedLinkedData::SearchQuery
2+
# Override Qa::Authorities::LinkedData::SearchQuery#search method
3+
# @return [String] json results for search query
4+
def search(query, language: nil, replacements: {}, subauth: nil, context: false, performance_data: false) # rubocop:disable Metrics/ParameterLists
5+
saved_performance_data = performance_data
6+
performance_data = true
7+
full_results = super
8+
QaServer::PerformanceHistory.save_result(dt_stamp: Time.now,
9+
authority: authority_name,
10+
action: 'search',
11+
size_bytes: full_results[:performance][:fetched_bytes],
12+
load_time_ms: (full_results[:performance][:fetch_time_s] * 1000),
13+
normalization_time_ms: (full_results[:performance][:normalization_time_s] * 1000))
14+
saved_performance_data ? full_results : full_results[:results]
15+
end
16+
end

0 commit comments

Comments
 (0)