qa_server 2.2.1 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/app/assets/stylesheets/qa_server/_monitor-status.scss +8 -0
- data/app/controllers/qa_server/check_status_controller.rb +1 -1
- data/app/controllers/qa_server/monitor_status_controller.rb +71 -13
- data/app/models/concerns/qa_server/performance_history_data_keys.rb +29 -0
- data/app/models/qa_server/performance_history.rb +75 -179
- data/app/prepends/prepended_linked_data/find_term.rb +1 -1
- data/app/prepends/prepended_linked_data/search_query.rb +1 -1
- data/app/presenters/concerns/qa_server/monitor_status/gruff_graph.rb +0 -1
- data/app/presenters/concerns/qa_server/monitor_status/performance_datatable_behavior.rb +101 -0
- data/app/presenters/concerns/qa_server/monitor_status/performance_graph_behavior.rb +109 -0
- data/app/presenters/qa_server/monitor_status/performance_presenter.rb +4 -220
- data/app/presenters/qa_server/monitor_status_presenter.rb +8 -5
- data/app/services/qa_server/performance_calculator_service.rb +103 -0
- data/app/services/qa_server/performance_graph_data_service.rb +113 -0
- data/app/services/qa_server/performance_graphing_service.rb +113 -0
- data/app/views/qa_server/monitor_status/_performance.html.erb +90 -0
- data/app/views/qa_server/monitor_status/_test_history.html.erb +32 -0
- data/app/views/qa_server/monitor_status/_test_summary.html.erb +54 -0
- data/app/views/qa_server/monitor_status/index.html.erb +3 -182
- data/config/locales/qa_server.en.yml +11 -7
- data/lib/generators/qa_server/templates/config/initializers/qa_server.rb +26 -0
- data/lib/qa_server/configuration.rb +42 -0
- data/lib/qa_server/version.rb +1 -1
- metadata +11 -2
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# This module provides methods for creating and accessing performance graphs.
|
3
|
+
module QaServer::MonitorStatus
|
4
|
+
module PerformanceGraphBehavior
|
5
|
+
include QaServer::PerformanceHistoryDataKeys
|
6
|
+
include QaServer::MonitorStatus::GruffGraph
|
7
|
+
|
8
|
+
def performance_graphs
|
9
|
+
auth_list = QaServer::AuthorityListerService.authorities_list
|
10
|
+
graphs = []
|
11
|
+
performance_graphs_for_authority(graphs, ALL_AUTH)
|
12
|
+
auth_list.each { |auth_name| performance_graphs_for_authority(graphs, auth_name) }
|
13
|
+
graphs
|
14
|
+
end
|
15
|
+
|
16
|
+
def performance_graph(graph_info)
|
17
|
+
graph_info[:graph]
|
18
|
+
end
|
19
|
+
|
20
|
+
def performance_graph_authority(graph_info)
|
21
|
+
graph_info[:authority_name]
|
22
|
+
end
|
23
|
+
|
24
|
+
def performance_graph_label(graph_info)
|
25
|
+
graph_info[:label]
|
26
|
+
end
|
27
|
+
|
28
|
+
def performance_default_graph_id
|
29
|
+
"performance-for-day-#{ALL_AUTH}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def performance_graph_id(graph_info)
|
33
|
+
"#{graph_info[:base_id]}-during-#{graph_info[:time_period]}-chart"
|
34
|
+
end
|
35
|
+
|
36
|
+
def performance_graph_data_section_id(graph_info)
|
37
|
+
"#{graph_info[:base_id]}-during-#{graph_info[:time_period]}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def performance_graph_data_section_base_id(graph_info)
|
41
|
+
graph_info[:base_id]
|
42
|
+
end
|
43
|
+
|
44
|
+
def performance_data_section_class(graph_info)
|
45
|
+
return 'performance-data-section-visible' if default_graph?(graph_info)
|
46
|
+
'performance-data-section-hidden'
|
47
|
+
end
|
48
|
+
|
49
|
+
def performance_day_graph_selected?(graph_info)
|
50
|
+
return true if graph_info[:time_period] == :day
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def performance_month_graph_selected?(graph_info)
|
55
|
+
return true if graph_info[:time_period] == :month
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def performance_year_graph_selected?(graph_info)
|
60
|
+
return true if graph_info[:time_period] == :year
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def default_graph?(graph_info) # rubocop:disable Metrics/CyclomaticComplexity
|
67
|
+
return true if QaServer.config.performance_graph_default_time_period == :day && performance_day_graph_selected?(graph_info)
|
68
|
+
return true if QaServer.config.performance_graph_default_time_period == :month && performance_month_graph_selected?(graph_info)
|
69
|
+
return true if QaServer.config.performance_graph_default_time_period == :year && performance_year_graph_selected?(graph_info)
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
def performance_graphs_for_authority(graphs, auth_name)
|
74
|
+
graphs << performance_for_day_graph(auth_name)
|
75
|
+
graphs << performance_for_month_graph(auth_name)
|
76
|
+
graphs << performance_for_year_graph(auth_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def performance_for_day_graph(auth_name)
|
80
|
+
{
|
81
|
+
time_period: :day,
|
82
|
+
graph: QaServer::PerformanceGraphingService.performance_graph_file(authority_name: auth_name, time_period: :day),
|
83
|
+
label: "Performance data for the last 24 hours.",
|
84
|
+
authority_name: auth_name,
|
85
|
+
base_id: "performance-for-#{auth_name}"
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def performance_for_month_graph(auth_name)
|
90
|
+
{
|
91
|
+
time_period: :month,
|
92
|
+
graph: QaServer::PerformanceGraphingService.performance_graph_file(authority_name: auth_name, time_period: :month),
|
93
|
+
label: "Performance data for the last 30 days.",
|
94
|
+
authority_name: auth_name,
|
95
|
+
base_id: "performance-for-#{auth_name}"
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def performance_for_year_graph(auth_name)
|
100
|
+
{
|
101
|
+
time_period: :year,
|
102
|
+
graph: QaServer::PerformanceGraphingService.performance_graph_file(authority_name: auth_name, time_period: :year),
|
103
|
+
label: "Performance data for the last 12 months.",
|
104
|
+
authority_name: auth_name,
|
105
|
+
base_id: "performance-for-#{auth_name}"
|
106
|
+
}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -1,35 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# This presenter class provides performance data needed by the view that monitors status of authorities.
|
3
3
|
module QaServer::MonitorStatus
|
4
|
-
class PerformancePresenter
|
5
|
-
class_attribute :performance_history_class
|
6
|
-
self.performance_history_class = QaServer::PerformanceHistory
|
7
|
-
|
4
|
+
class PerformancePresenter
|
8
5
|
include QaServer::MonitorStatus::GruffGraph
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
FOR_LIFETIME = performance_history_class::PERFORMANCE_FOR_LIFETIME_KEY
|
14
|
-
FOR_DAY = performance_history_class::PERFORMANCE_FOR_DAY_KEY
|
15
|
-
BY_HOUR = performance_history_class::PERFORMANCE_BY_HOUR_KEY
|
16
|
-
FOR_MONTH = performance_history_class::PERFORMANCE_FOR_MONTH_KEY
|
17
|
-
BY_DAY = performance_history_class::PERFORMANCE_BY_DAY_KEY
|
18
|
-
FOR_YEAR = performance_history_class::PERFORMANCE_FOR_YEAR_KEY
|
19
|
-
BY_MONTH = performance_history_class::PERFORMANCE_BY_MONTH_KEY
|
20
|
-
|
21
|
-
SUM_LOAD = performance_history_class::SUM_LOAD_TIME_KEY
|
22
|
-
SUM_NORMALIZATION = performance_history_class::SUM_NORMALIZATION_TIME_KEY
|
23
|
-
SUM_FULL_REQUEST = performance_history_class::SUM_FULL_REQUEST_TIME_KEY
|
24
|
-
MIN_LOAD = performance_history_class::MIN_LOAD_TIME_KEY
|
25
|
-
MIN_NORMALIZATION = performance_history_class::MIN_NORMALIZATION_TIME_KEY
|
26
|
-
MIN_FULL_REQUEST = performance_history_class::MIN_FULL_REQUEST_TIME_KEY
|
27
|
-
MAX_LOAD = performance_history_class::MAX_LOAD_TIME_KEY
|
28
|
-
MAX_NORMALIZATION = performance_history_class::MAX_NORMALIZATION_TIME_KEY
|
29
|
-
MAX_FULL_REQUEST = performance_history_class::MAX_FULL_REQUEST_TIME_KEY
|
30
|
-
AVG_LOAD = performance_history_class::AVG_LOAD_TIME_KEY
|
31
|
-
AVG_NORMALIZATION = performance_history_class::AVG_NORMALIZATION_TIME_KEY
|
32
|
-
AVG_FULL_REQUEST = performance_history_class::AVG_FULL_REQUEST_TIME_KEY
|
6
|
+
include QaServer::MonitorStatus::PerformanceDatatableBehavior
|
7
|
+
include QaServer::MonitorStatus::PerformanceGraphBehavior
|
8
|
+
include QaServer::PerformanceHistoryDataKeys
|
33
9
|
|
34
10
|
# @param performance_data [Hash<Hash>] performance data
|
35
11
|
def initialize(performance_data:)
|
@@ -57,197 +33,5 @@ module QaServer::MonitorStatus
|
|
57
33
|
def performance_data_authority_name(entry)
|
58
34
|
entry.keys.first
|
59
35
|
end
|
60
|
-
|
61
|
-
def performance_for_day_graph
|
62
|
-
performance_graph_file(rework_performance_data_for_gruff(all_authorities_performance_data[FOR_DAY], BY_HOUR),
|
63
|
-
performance_for_day_graph_full_path,
|
64
|
-
performance_for_day_graph_filename,
|
65
|
-
I18n.t('qa_server.monitor_status.performance.x_axis_hour'))
|
66
|
-
end
|
67
|
-
|
68
|
-
def performance_for_month_graph
|
69
|
-
performance_graph_file(rework_performance_data_for_gruff(all_authorities_performance_data[FOR_MONTH], BY_DAY),
|
70
|
-
performance_for_month_graph_full_path,
|
71
|
-
performance_for_month_graph_filename,
|
72
|
-
I18n.t('qa_server.monitor_status.performance.x_axis_day'))
|
73
|
-
end
|
74
|
-
|
75
|
-
def performance_for_year_graph
|
76
|
-
performance_graph_file(rework_performance_data_for_gruff(all_authorities_performance_data[FOR_YEAR], BY_MONTH),
|
77
|
-
performance_for_year_graph_full_path,
|
78
|
-
performance_for_year_graph_filename,
|
79
|
-
I18n.t('qa_server.monitor_status.performance.x_axis_month'))
|
80
|
-
end
|
81
|
-
|
82
|
-
def lifetime_stats(authority_data)
|
83
|
-
authority_data[FOR_LIFETIME]
|
84
|
-
end
|
85
|
-
|
86
|
-
def min_load(stats)
|
87
|
-
format_stat stats[MIN_LOAD]
|
88
|
-
end
|
89
|
-
|
90
|
-
def min_normalization(stats)
|
91
|
-
format_stat stats[MIN_NORMALIZATION]
|
92
|
-
end
|
93
|
-
|
94
|
-
def min_full_request(stats)
|
95
|
-
format_stat stats[MIN_FULL_REQUEST]
|
96
|
-
end
|
97
|
-
|
98
|
-
def max_load(stats)
|
99
|
-
format_stat stats[MAX_LOAD]
|
100
|
-
end
|
101
|
-
|
102
|
-
def max_normalization(stats)
|
103
|
-
format_stat stats[MAX_NORMALIZATION]
|
104
|
-
end
|
105
|
-
|
106
|
-
def max_full_request(stats)
|
107
|
-
format_stat stats[MAX_FULL_REQUEST]
|
108
|
-
end
|
109
|
-
|
110
|
-
def avg_load(stats)
|
111
|
-
format_stat stats[AVG_LOAD]
|
112
|
-
end
|
113
|
-
|
114
|
-
def avg_normalization(stats)
|
115
|
-
format_stat stats[AVG_NORMALIZATION]
|
116
|
-
end
|
117
|
-
|
118
|
-
def avg_full_request(stats)
|
119
|
-
format_stat stats[AVG_FULL_REQUEST]
|
120
|
-
end
|
121
|
-
|
122
|
-
def min_load_style(stats)
|
123
|
-
performance_style_class(stats, MIN_LOAD)
|
124
|
-
end
|
125
|
-
|
126
|
-
def min_normalization_style(stats)
|
127
|
-
performance_style_class(stats, MIN_NORMALIZATION)
|
128
|
-
end
|
129
|
-
|
130
|
-
def min_full_request_style(stats)
|
131
|
-
performance_style_class(stats, MIN_FULL_REQUEST)
|
132
|
-
end
|
133
|
-
|
134
|
-
def max_load_style(stats)
|
135
|
-
performance_style_class(stats, MAX_LOAD)
|
136
|
-
end
|
137
|
-
|
138
|
-
def max_normalization_style(stats)
|
139
|
-
performance_style_class(stats, MAX_NORMALIZATION)
|
140
|
-
end
|
141
|
-
|
142
|
-
def max_full_request_style(stats)
|
143
|
-
performance_style_class(stats, MAX_FULL_REQUEST)
|
144
|
-
end
|
145
|
-
|
146
|
-
def avg_load_style(stats)
|
147
|
-
performance_style_class(stats, AVG_LOAD)
|
148
|
-
end
|
149
|
-
|
150
|
-
def avg_normalization_style(stats)
|
151
|
-
performance_style_class(stats, AVG_NORMALIZATION)
|
152
|
-
end
|
153
|
-
|
154
|
-
def avg_full_request_style(stats)
|
155
|
-
performance_style_class(stats, AVG_FULL_REQUEST)
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
def all_authorities_performance_data
|
161
|
-
performance_data[ALL_AUTHS]
|
162
|
-
end
|
163
|
-
|
164
|
-
def format_stat(stat)
|
165
|
-
format("%0.1f", stat)
|
166
|
-
end
|
167
|
-
|
168
|
-
def performance_style_class(stats, stat_key)
|
169
|
-
return "status-bad" if max_threshold_exceeded(stats, stat_key)
|
170
|
-
return "status-unknown" if min_threshold_not_met(stats, stat_key)
|
171
|
-
"status-neutral"
|
172
|
-
end
|
173
|
-
|
174
|
-
MAX_THRESHOLD = 1000 # ms
|
175
|
-
def max_threshold_exceeded(stats, stat_key)
|
176
|
-
return true if stats[stat_key] > MAX_THRESHOLD
|
177
|
-
false
|
178
|
-
end
|
179
|
-
|
180
|
-
MIN_THRESHOLD = 500 # ms
|
181
|
-
def min_threshold_not_met(stats, stat_key)
|
182
|
-
return true unless stats[stat_key] < MIN_THRESHOLD
|
183
|
-
false
|
184
|
-
end
|
185
|
-
|
186
|
-
def performance_graph_theme(g, x_axis_label)
|
187
|
-
g.theme_pastel
|
188
|
-
g.colors = ['#81adf4', '#8696b0', '#06578a']
|
189
|
-
g.marker_font_size = 12
|
190
|
-
g.x_axis_increment = 10
|
191
|
-
g.x_axis_label = x_axis_label
|
192
|
-
g.y_axis_label = I18n.t('qa_server.monitor_status.performance.y_axis_ms')
|
193
|
-
g.dot_radius = 3
|
194
|
-
g.line_width = 2
|
195
|
-
g.minimum_value = 0
|
196
|
-
g.maximum_value = 1000
|
197
|
-
end
|
198
|
-
|
199
|
-
def graph_filename(authority_name, time_period)
|
200
|
-
"performance_of_#{authority_name}_for_#{time_period}_graph.png"
|
201
|
-
end
|
202
|
-
|
203
|
-
def performance_for_day_graph_filename
|
204
|
-
graph_filename(ALL_AUTHS, :day)
|
205
|
-
end
|
206
|
-
|
207
|
-
def performance_for_day_graph_full_path
|
208
|
-
graph_full_path(performance_for_day_graph_filename)
|
209
|
-
end
|
210
|
-
|
211
|
-
def performance_for_month_graph_filename
|
212
|
-
graph_filename(ALL_AUTHS, :month)
|
213
|
-
end
|
214
|
-
|
215
|
-
def performance_for_month_graph_full_path
|
216
|
-
graph_full_path(performance_for_month_graph_filename)
|
217
|
-
end
|
218
|
-
|
219
|
-
def performance_for_year_graph_filename
|
220
|
-
graph_filename(ALL_AUTHS, :year)
|
221
|
-
end
|
222
|
-
|
223
|
-
def performance_for_year_graph_full_path
|
224
|
-
graph_full_path(performance_for_year_graph_filename)
|
225
|
-
end
|
226
|
-
|
227
|
-
def rework_performance_data_for_gruff(performance_data, label_key)
|
228
|
-
labels = {}
|
229
|
-
load_data = []
|
230
|
-
normalization_data = []
|
231
|
-
full_request_data = []
|
232
|
-
performance_data.each do |i, data|
|
233
|
-
labels[i] = data[label_key]
|
234
|
-
load_data << data[STATS][AVG_LOAD]
|
235
|
-
normalization_data << data[STATS][AVG_NORMALIZATION]
|
236
|
-
full_request_data << data[STATS][AVG_FULL_REQUEST]
|
237
|
-
end
|
238
|
-
[labels, load_data, normalization_data, full_request_data]
|
239
|
-
end
|
240
|
-
|
241
|
-
def performance_graph_file(performance_data, performance_graph_full_path, performance_graph_filename, x_axis_label)
|
242
|
-
g = Gruff::Line.new
|
243
|
-
performance_graph_theme(g, x_axis_label)
|
244
|
-
g.title = ''
|
245
|
-
g.labels = performance_data[0]
|
246
|
-
g.data(I18n.t('qa_server.monitor_status.performance.load_time_ms'), performance_data[1])
|
247
|
-
g.data(I18n.t('qa_server.monitor_status.performance.normalization_time_ms'), performance_data[2])
|
248
|
-
g.data(I18n.t('qa_server.monitor_status.performance.full_request_time_ms'), performance_data[3])
|
249
|
-
g.write performance_graph_full_path
|
250
|
-
File.join(graph_relative_path, performance_graph_filename)
|
251
|
-
end
|
252
36
|
end
|
253
37
|
end
|
@@ -25,10 +25,13 @@ module QaServer
|
|
25
25
|
|
26
26
|
def_delegators :@performance_presenter, :performance_data, :performance_data?, :display_performance?, :display_performance_graph?,
|
27
27
|
:display_performance_datatable?, :performance_data_authority_name, :performance_for_day_graph, :performance_for_month_graph,
|
28
|
-
:performance_for_year_graph, :
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:avg_load_style, :avg_normalization_style, :avg_full_request_style, :performance_style_class, :
|
32
|
-
:
|
28
|
+
:performance_for_year_graph, :datatable_stats, :low_load, :low_normalization, :low_full_request, :high_load,
|
29
|
+
:high_normalization, :high_full_request, :avg_load, :avg_normalization, :avg_full_request, :low_load_style,
|
30
|
+
:low_normalization_style, :low_full_request_style, :high_load_style, :high_normalization_style, :high_full_request_style,
|
31
|
+
:avg_load_style, :avg_normalization_style, :avg_full_request_style, :performance_style_class, :high_threshold_exceeded,
|
32
|
+
:low_threshold_not_met, :performance_graphs, :performance_graph, :performance_graph_authority, :performance_graph_label,
|
33
|
+
:performance_default_graph_id, :performance_graph_id, :performance_graph_data_section_id, :performance_graph_data_section_base_id,
|
34
|
+
:performance_data_section_class, :performance_day_graph_selected?, :performance_month_graph_selected?,
|
35
|
+
:performance_year_graph_selected?, :performance_table_description
|
33
36
|
end
|
34
37
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# This class calculates min, max, average stats for load, normalization, and full request times for a given set of performance records.
|
3
|
+
require 'matrix'
|
4
|
+
module QaServer
|
5
|
+
class PerformanceCalculatorService
|
6
|
+
include QaServer::PerformanceHistoryDataKeys
|
7
|
+
|
8
|
+
attr_reader :records
|
9
|
+
attr_reader :stats
|
10
|
+
|
11
|
+
# @param records [Array <Qa::PerformanceHistory>] set of records used to calculate the statistics
|
12
|
+
def initialize(records)
|
13
|
+
@records = records
|
14
|
+
@stats = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Calculate performance statistics for a set of PerformanceHistory records. Min is at the 10th percentile. Max is at the 90th percentile.
|
18
|
+
# @return [Hash] hash of the statistics
|
19
|
+
# @example
|
20
|
+
# { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
|
21
|
+
# load_min_ms: 12.3, normalization_min_ms: 4.2, full_request_min_ms: 16.5,
|
22
|
+
# load_max_ms: 12.3, normalization_max_ms: 4.2, full_request_max_ms: 16.5 }
|
23
|
+
def calculate_stats(avg: false, low: false, high: false, load: true, norm: true, full: true) # rubocop:disable Metrics/ParameterLists
|
24
|
+
calculate_load_stats(avg, low, high) if load
|
25
|
+
calculate_norm_stats(avg, low, high) if norm
|
26
|
+
calculate_full_stats(avg, low, high) if full
|
27
|
+
stats
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def calculate_load_stats(avg, low, high)
|
33
|
+
stats[AVG_LOAD] = calculate_average(load_times) if avg
|
34
|
+
stats[LOW_LOAD] = calculate_10th_percentile(load_times_sorted) if low
|
35
|
+
stats[HIGH_LOAD] = calculate_90th_percentile(load_times_sorted) if high
|
36
|
+
end
|
37
|
+
|
38
|
+
def calculate_norm_stats(avg, low, high)
|
39
|
+
stats[AVG_NORM] = calculate_average(norm_times) if avg
|
40
|
+
stats[LOW_NORM] = calculate_10th_percentile(norm_times_sorted) if low
|
41
|
+
stats[HIGH_NORM] = calculate_90th_percentile(norm_times_sorted) if high
|
42
|
+
end
|
43
|
+
|
44
|
+
def calculate_full_stats(avg, low, high)
|
45
|
+
stats[AVG_FULL] = calculate_average(full_times) if avg
|
46
|
+
stats[LOW_FULL] = calculate_10th_percentile(full_times_sorted) if low
|
47
|
+
stats[HIGH_FULL] = calculate_90th_percentile(full_times_sorted) if high
|
48
|
+
end
|
49
|
+
|
50
|
+
def count
|
51
|
+
@count ||= records.count
|
52
|
+
end
|
53
|
+
|
54
|
+
def tenth_percentile_count
|
55
|
+
return @tenth_percentile_count if @tenth_percentile_count.present?
|
56
|
+
percentile_count = (count * 0.1).round
|
57
|
+
percentile_count = 1 if percentile_count.zero? && count > 1
|
58
|
+
@tenth_percentile_count = percentile_count
|
59
|
+
end
|
60
|
+
|
61
|
+
def load_times
|
62
|
+
@load_times ||= records.pluck(:load_time_ms).to_a
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_times_sorted
|
66
|
+
@load_times_sorted ||= load_times.sort
|
67
|
+
end
|
68
|
+
|
69
|
+
def norm_times
|
70
|
+
@norm_times ||= records.pluck(:normalization_time_ms).to_a
|
71
|
+
end
|
72
|
+
|
73
|
+
def norm_times_sorted
|
74
|
+
@norm_times_sorted ||= norm_times.sort
|
75
|
+
end
|
76
|
+
|
77
|
+
def full_times
|
78
|
+
@full_times ||= (Vector.elements(load_times) + Vector.elements(norm_times)).to_a
|
79
|
+
end
|
80
|
+
|
81
|
+
def full_times_sorted
|
82
|
+
@full_times_sorted ||= full_times.sort
|
83
|
+
end
|
84
|
+
|
85
|
+
def calculate_average(times)
|
86
|
+
return 0 if count.zero?
|
87
|
+
return times[0] if count == 1
|
88
|
+
times.inject(0.0) { |sum, el| sum + el } / count
|
89
|
+
end
|
90
|
+
|
91
|
+
def calculate_10th_percentile(sorted_times)
|
92
|
+
return 0 if count.zero?
|
93
|
+
return sorted_times[0] if count == 1
|
94
|
+
sorted_times[tenth_percentile_count - 1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def calculate_90th_percentile(sorted_times)
|
98
|
+
return 0 if count.zero?
|
99
|
+
return sorted_times[0] if count == 1
|
100
|
+
sorted_times[count - tenth_percentile_count]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# This class sets performance stats for the last 24 hours, past 30 days, and the past 12 months.
|
3
|
+
module QaServer
|
4
|
+
class PerformanceGraphDataService
|
5
|
+
class << self
|
6
|
+
include QaServer::PerformanceHistoryDataKeys
|
7
|
+
|
8
|
+
class_attribute :stats_calculator_class, :performance_data_class
|
9
|
+
self.stats_calculator_class = QaServer::PerformanceCalculatorService
|
10
|
+
self.performance_data_class = QaServer::PerformanceHistory
|
11
|
+
|
12
|
+
# Get hourly average for the past 24 hours.
|
13
|
+
# @returns [Hash] performance statistics for the past 24 hours
|
14
|
+
# @example
|
15
|
+
# { 0: { hour: '1400', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
16
|
+
# 1: { hour: '1500', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
17
|
+
# 2: { hour: '1600', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
18
|
+
# ...,
|
19
|
+
# 23: { hour: 'NOW', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
20
|
+
# }
|
21
|
+
def average_last_24_hours(auth_name = nil)
|
22
|
+
start_hour = Time.now.getlocal.beginning_of_hour - 23.hours
|
23
|
+
avgs = {}
|
24
|
+
0.upto(23).each do |idx|
|
25
|
+
where_clause = { dt_stamp: start_hour..start_hour.end_of_hour }
|
26
|
+
where_clause[:authority] = auth_name unless auth_name.nil?
|
27
|
+
records = performance_data_class.where(where_clause)
|
28
|
+
stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
|
29
|
+
data = {}
|
30
|
+
data[BY_HOUR] = performance_by_hour_label(idx, start_hour)
|
31
|
+
data[STATS] = stats
|
32
|
+
avgs[idx] = data
|
33
|
+
start_hour += 1.hour
|
34
|
+
end
|
35
|
+
avgs
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get daily average for the past 30 days.
|
39
|
+
# @returns [Hash] performance statistics for the past 30 days
|
40
|
+
# @example
|
41
|
+
# { 0: { day: '07-15-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
42
|
+
# 1: { day: '07-16-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
43
|
+
# 2: { day: '07-17-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
44
|
+
# ...,
|
45
|
+
# 29: { day: 'TODAY', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
46
|
+
# }
|
47
|
+
def average_last_30_days(auth_name = nil)
|
48
|
+
start_day = Time.now.getlocal.beginning_of_day - 29.days
|
49
|
+
avgs = {}
|
50
|
+
0.upto(29).each do |idx|
|
51
|
+
where_clause = { dt_stamp: start_day..start_day.end_of_day }
|
52
|
+
where_clause[:authority] = auth_name unless auth_name.nil?
|
53
|
+
records = performance_data_class.where(where_clause)
|
54
|
+
stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
|
55
|
+
data = {}
|
56
|
+
data[BY_DAY] = performance_by_day_label(idx, start_day)
|
57
|
+
data[STATS] = stats
|
58
|
+
avgs[idx] = data
|
59
|
+
start_day += 1.day
|
60
|
+
end
|
61
|
+
avgs
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get daily average for the past 12 months.
|
65
|
+
# @returns [Hash] performance statistics for the past 12 months
|
66
|
+
# @example
|
67
|
+
# { 0: { month: '09-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
68
|
+
# 1: { month: '10-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
69
|
+
# 2: { month: '11-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
70
|
+
# ...,
|
71
|
+
# 11: { month: '08-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
72
|
+
# }
|
73
|
+
def average_last_12_months(auth_name = nil)
|
74
|
+
start_month = Time.now.getlocal.beginning_of_month - 11.months
|
75
|
+
avgs = {}
|
76
|
+
0.upto(11).each do |idx|
|
77
|
+
where_clause = { dt_stamp: start_month..start_month.end_of_month }
|
78
|
+
where_clause[:authority] = auth_name unless auth_name.nil?
|
79
|
+
records = performance_data_class.where(where_clause)
|
80
|
+
stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
|
81
|
+
data = {}
|
82
|
+
data[BY_MONTH] = start_month.strftime("%m-%Y")
|
83
|
+
data[STATS] = stats
|
84
|
+
avgs[idx] = data
|
85
|
+
start_month += 1.month
|
86
|
+
end
|
87
|
+
avgs
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def performance_by_hour_label(idx, start_hour)
|
93
|
+
if idx == 23
|
94
|
+
I18n.t('qa_server.monitor_status.performance.now')
|
95
|
+
elsif ((idx + 1) % 2).zero?
|
96
|
+
(start_hour.hour * 100).to_s
|
97
|
+
else
|
98
|
+
""
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def performance_by_day_label(idx, start_day)
|
103
|
+
if idx == 29
|
104
|
+
I18n.t('qa_server.monitor_status.performance.today')
|
105
|
+
elsif ((idx + 1) % 5).zero?
|
106
|
+
start_day.strftime("%m-%d")
|
107
|
+
else
|
108
|
+
""
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|