qa_server 2.2.1 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|