qa_server 5.5.1 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +53 -0
- data/app/controllers/{qa_server/authority_validation_controller.rb → concerns/qa_server/authority_validation_behavior.rb} +13 -11
- data/app/controllers/qa_server/authority_list_controller.rb +5 -1
- data/app/controllers/qa_server/check_status_controller.rb +5 -1
- data/app/controllers/qa_server/monitor_status_controller.rb +40 -28
- data/app/jobs/qa_server/monitor_tests_job.rb +50 -0
- data/app/models/qa_server/performance_cache.rb +11 -3
- data/app/models/qa_server/performance_history.rb +24 -106
- data/app/models/qa_server/scenario_run_history.rb +161 -176
- data/app/models/qa_server/scenario_run_registry.rb +4 -4
- data/app/prepends/prepended_linked_data/find_term.rb +4 -4
- data/app/prepends/prepended_linked_data/search_query.rb +4 -4
- data/app/prepends/prepended_rdf/rdf_graph.rb +4 -4
- data/app/presenters/concerns/qa_server/monitor_status/performance_datatable_behavior.rb +23 -1
- data/app/presenters/qa_server/check_status_presenter.rb +4 -4
- data/app/presenters/qa_server/monitor_status/current_status_presenter.rb +17 -5
- data/app/presenters/qa_server/monitor_status/history_presenter.rb +40 -19
- data/app/presenters/qa_server/monitor_status/performance_presenter.rb +3 -1
- data/app/presenters/qa_server/monitor_status_presenter.rb +9 -9
- data/app/services/qa_server/monitor_cache_service.rb +22 -0
- data/app/services/qa_server/performance_calculator_service.rb +18 -7
- data/app/services/qa_server/performance_datatable_service.rb +82 -0
- data/app/services/qa_server/performance_graph_data_service.rb +140 -82
- data/app/services/qa_server/performance_graphing_service.rb +15 -12
- data/app/services/qa_server/time_period_service.rb +93 -0
- data/app/services/qa_server/time_service.rb +29 -0
- data/app/validators/qa_server/scenario_validator.rb +3 -3
- data/app/validators/qa_server/search_scenario_validator.rb +3 -3
- data/app/views/qa_server/monitor_status/_performance.html.erb +2 -1
- data/app/views/qa_server/monitor_status/_test_history.html.erb +1 -2
- data/app/views/qa_server/monitor_status/_test_summary.html.erb +2 -2
- data/config/locales/qa_server.en.yml +3 -4
- data/lib/generators/qa_server/templates/config/initializers/qa_server.rb +4 -0
- data/lib/qa_server.rb +0 -23
- data/lib/qa_server/configuration.rb +20 -0
- data/lib/qa_server/version.rb +1 -1
- data/spec/lib/qa_server_spec.rb +0 -51
- data/spec/services/qa_server/monitor_cache_service_spec.rb +20 -0
- data/spec/services/qa_server/time_period_service_spec.rb +246 -0
- data/spec/services/qa_server/time_service_spec.rb +50 -0
- metadata +14 -3
@@ -17,15 +17,15 @@ module PrependedRdf::RdfGraph
|
|
17
17
|
raise TypeError, "#{self} is immutable" if immutable?
|
18
18
|
phid, real_url = parse_phid(url)
|
19
19
|
performance_udpates = {}
|
20
|
-
start_time_s = QaServer.current_time_s
|
20
|
+
start_time_s = QaServer::TimeService.current_time_s
|
21
21
|
|
22
22
|
reader = RDF::Reader.open(real_url, { base_uri: real_url }.merge(options))
|
23
23
|
|
24
|
-
end_time_s = QaServer.current_time_s
|
24
|
+
end_time_s = QaServer::TimeService.current_time_s
|
25
25
|
performance_udpates[:retrieve_time_ms] = (end_time_s - start_time_s) * 1000
|
26
26
|
QaServer.config.performance_tracker.write "#{format('%.6f', end_time_s - start_time_s)}, " # read data
|
27
27
|
|
28
|
-
start_time_s = QaServer.current_time_s
|
28
|
+
start_time_s = QaServer::TimeService.current_time_s
|
29
29
|
|
30
30
|
if graph_name
|
31
31
|
statements = []
|
@@ -40,7 +40,7 @@ module PrependedRdf::RdfGraph
|
|
40
40
|
nil
|
41
41
|
end
|
42
42
|
|
43
|
-
end_time_s = QaServer.current_time_s
|
43
|
+
end_time_s = QaServer::TimeService.current_time_s
|
44
44
|
performance_udpates[:graph_load_time_ms] = (end_time_s - start_time_s) * 1000
|
45
45
|
QaServer.config.performance_cache.update(id: phid, updates: performance_udpates)
|
46
46
|
QaServer.config.performance_tracker.write "#{format('%.6f', end_time_s - start_time_s)}, " # load graph
|
@@ -94,6 +94,28 @@ module QaServer::MonitorStatus
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
def performance_data_start
|
98
|
+
start_dt = case expected_time_period
|
99
|
+
when :day
|
100
|
+
performance_data_end_dt - 1.day
|
101
|
+
when :month
|
102
|
+
performance_data_end_dt - 1.month
|
103
|
+
when :year
|
104
|
+
performance_data_end_dt - 1.year
|
105
|
+
else
|
106
|
+
@parent.first_updated_dt
|
107
|
+
end
|
108
|
+
QaServer::TimeService.pretty_date(start_dt)
|
109
|
+
end
|
110
|
+
|
111
|
+
def performance_data_end_dt
|
112
|
+
@parent.last_updated_dt
|
113
|
+
end
|
114
|
+
|
115
|
+
def performance_data_end
|
116
|
+
QaServer::TimeService.pretty_date(performance_data_end_dt)
|
117
|
+
end
|
118
|
+
|
97
119
|
private
|
98
120
|
|
99
121
|
def expected_time_period
|
@@ -101,7 +123,7 @@ module QaServer::MonitorStatus
|
|
101
123
|
end
|
102
124
|
|
103
125
|
def data_table_for(authority_data, action)
|
104
|
-
authority_data[action]
|
126
|
+
authority_data[action]
|
105
127
|
end
|
106
128
|
|
107
129
|
def unsupported_action?(stats)
|
@@ -76,11 +76,11 @@ module QaServer
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def value_check_param
|
79
|
-
QaServer::
|
79
|
+
QaServer::AuthorityValidationBehavior::VALIDATION_TYPE_PARAM
|
80
80
|
end
|
81
81
|
|
82
82
|
def value_check_connections
|
83
|
-
QaServer::
|
83
|
+
QaServer::AuthorityValidationBehavior::VALIDATE_CONNECTIONS
|
84
84
|
end
|
85
85
|
|
86
86
|
def label_check_connections
|
@@ -88,7 +88,7 @@ module QaServer
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def value_check_accuracy
|
91
|
-
QaServer::
|
91
|
+
QaServer::AuthorityValidationBehavior::VALIDATE_ACCURACY
|
92
92
|
end
|
93
93
|
|
94
94
|
def label_check_accuracy
|
@@ -96,7 +96,7 @@ module QaServer
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def value_all_checks
|
99
|
-
QaServer::
|
99
|
+
QaServer::AuthorityValidationBehavior::ALL_VALIDATIONS
|
100
100
|
end
|
101
101
|
|
102
102
|
def label_all_checks
|
@@ -2,23 +2,35 @@
|
|
2
2
|
# This presenter class provides data related to last test run as needed by the view that monitors status of authorities.
|
3
3
|
module QaServer::MonitorStatus
|
4
4
|
class CurrentStatusPresenter
|
5
|
+
# @param parent [QaServer::MonitorStatusPresenter] parent presenter
|
5
6
|
# @param current_summary [ScenarioRunSummary] summary status of the latest run of test scenarios
|
6
7
|
# @param current_data [Array<Hash>] current set of failures for the latest test run, if any
|
7
|
-
def initialize(current_summary:, current_failure_data:)
|
8
|
+
def initialize(parent:, current_summary:, current_failure_data:)
|
9
|
+
@parent = parent
|
8
10
|
@current_summary = current_summary
|
9
11
|
@current_failure_data = current_failure_data
|
10
12
|
end
|
11
13
|
|
12
|
-
# @return [
|
14
|
+
# @return [ActiveSupport::TimeWithZone] date time stamp of last test run
|
15
|
+
def last_updated_dt
|
16
|
+
@current_summary.run_dt_stamp
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] date with time of last test run
|
13
20
|
def last_updated
|
14
|
-
|
21
|
+
QaServer::TimeService.pretty_time(last_updated_dt)
|
15
22
|
end
|
16
23
|
|
17
|
-
# @return [
|
18
|
-
def
|
24
|
+
# @return [ActiveSupport::TimeWithZone] date time stamp of first recorded test run
|
25
|
+
def first_updated_dt
|
19
26
|
QaServer::ScenarioRunRegistry.first_run_dt
|
20
27
|
end
|
21
28
|
|
29
|
+
# @return [String] date with time of first recorded test run
|
30
|
+
def first_updated
|
31
|
+
QaServer::TimeService.pretty_time(first_updated_dt)
|
32
|
+
end
|
33
|
+
|
22
34
|
# @return [Integer] number of loaded authorities
|
23
35
|
def authorities_count
|
24
36
|
@current_summary.authority_count
|
@@ -1,15 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# This presenter class provides historical testing data needed by the view that monitors status of authorities.
|
3
3
|
module QaServer::MonitorStatus
|
4
|
-
class HistoryPresenter
|
5
|
-
HISTORICAL_AUTHORITY_NAME_IDX = 0
|
6
|
-
HISTORICAL_FAILURE_COUNT_IDX = 1
|
7
|
-
HISTORICAL_PASSING_COUNT_IDX = 2
|
8
|
-
|
4
|
+
class HistoryPresenter # rubocop:disable Metrics/ClassLength
|
9
5
|
include QaServer::MonitorStatus::GruffGraph
|
10
6
|
|
7
|
+
# @param parent [QaServer::MonitorStatusPresenter] parent presenter
|
11
8
|
# @param historical_summary_data [Array<Hash>] summary of past failuring runs per authority to drive chart
|
12
|
-
def initialize(historical_summary_data:)
|
9
|
+
def initialize(parent:, historical_summary_data:)
|
10
|
+
@parent = parent
|
13
11
|
@historical_summary_data = historical_summary_data
|
14
12
|
end
|
15
13
|
|
@@ -23,8 +21,33 @@ module QaServer::MonitorStatus
|
|
23
21
|
|
24
22
|
# @return [Boolean] true if historical test data exists; otherwise false
|
25
23
|
def history?
|
26
|
-
|
27
|
-
|
24
|
+
@historical_summary_data.present?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return the first date of data represented in the history graph and data table
|
28
|
+
# @return [String] string version of date formatted with just date (e.g. "02/01/2020")
|
29
|
+
def history_start
|
30
|
+
start_dt = case QaServer.config.historical_datatable_default_time_period
|
31
|
+
when :month
|
32
|
+
history_end_dt - 1.month
|
33
|
+
when :year
|
34
|
+
history_end_dt - 1.year
|
35
|
+
else
|
36
|
+
@parent.first_updated_dt
|
37
|
+
end
|
38
|
+
QaServer::TimeService.pretty_date(start_dt)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return the last date of data represented in the history graph and data table
|
42
|
+
# @return [ActiveSupport::TimeWithZone] date time stamp
|
43
|
+
def history_end_dt
|
44
|
+
@parent.last_updated_dt
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the last date of data represented in the history graph and data table
|
48
|
+
# @return [String] string version of date formatted with just date (e.g. "02/01/2020")
|
49
|
+
def history_end
|
50
|
+
QaServer::TimeService.pretty_date(history_end_dt)
|
28
51
|
end
|
29
52
|
|
30
53
|
def historical_graph
|
@@ -57,15 +80,15 @@ module QaServer::MonitorStatus
|
|
57
80
|
end
|
58
81
|
|
59
82
|
def historical_data_authority_name(historical_entry)
|
60
|
-
historical_entry[
|
83
|
+
historical_entry[0]
|
61
84
|
end
|
62
85
|
|
63
86
|
def days_authority_passing(historical_entry)
|
64
|
-
historical_entry[
|
87
|
+
historical_entry[1]["good"]
|
65
88
|
end
|
66
89
|
|
67
90
|
def days_authority_failing(historical_entry)
|
68
|
-
historical_entry[
|
91
|
+
historical_entry[1]["bad"]
|
69
92
|
end
|
70
93
|
|
71
94
|
def days_authority_tested(historical_entry)
|
@@ -82,13 +105,11 @@ module QaServer::MonitorStatus
|
|
82
105
|
|
83
106
|
def failure_style_class(historical_entry)
|
84
107
|
return "status-neutral" if days_authority_failing(historical_entry) <= 0
|
85
|
-
|
86
|
-
"status-bad"
|
108
|
+
percent_authority_failing(historical_entry) < 0.1 ? "status-unknown" : "status-bad"
|
87
109
|
end
|
88
110
|
|
89
111
|
def passing_style_class(historical_entry)
|
90
|
-
|
91
|
-
"status-good"
|
112
|
+
days_authority_passing(historical_entry) <= 0 ? "status-bad" : "status-good"
|
92
113
|
end
|
93
114
|
|
94
115
|
def display_history_details?
|
@@ -125,11 +146,11 @@ module QaServer::MonitorStatus
|
|
125
146
|
pass_data = []
|
126
147
|
fail_data = []
|
127
148
|
i = 0
|
128
|
-
historical_summary.each do |data|
|
129
|
-
labels[i] =
|
149
|
+
historical_summary.each do |auth, data|
|
150
|
+
labels[i] = auth
|
130
151
|
i += 1
|
131
|
-
fail_data << data[
|
132
|
-
pass_data << data[
|
152
|
+
fail_data << data["bad"]
|
153
|
+
pass_data << data["good"]
|
133
154
|
end
|
134
155
|
[labels, fail_data, pass_data]
|
135
156
|
end
|
@@ -7,8 +7,10 @@ module QaServer::MonitorStatus
|
|
7
7
|
include QaServer::MonitorStatus::PerformanceGraphBehavior
|
8
8
|
include QaServer::PerformanceHistoryDataKeys
|
9
9
|
|
10
|
+
# @param parent [QaServer::MonitorStatusPresenter] parent presenter
|
10
11
|
# @param performance_data [Hash<Hash>] performance data
|
11
|
-
def initialize(performance_data:)
|
12
|
+
def initialize(parent:, performance_data:)
|
13
|
+
@parent = parent
|
12
14
|
@performance_data = performance_data
|
13
15
|
end
|
14
16
|
|
@@ -7,21 +7,21 @@ module QaServer
|
|
7
7
|
# @param current_summary [ScenarioRunSummary] summary status of the latest run of test scenarios
|
8
8
|
# @param current_data [Array<Hash>] current set of failures for the latest test run, if any
|
9
9
|
# @param historical_summary_data [Array<Hash>] summary of past failuring runs per authority to drive chart
|
10
|
-
# @param performance_data [Hash<Hash>] performance data
|
10
|
+
# @param performance_data [Hash<Hash>] performance datatable data
|
11
11
|
def initialize(current_summary:, current_failure_data:, historical_summary_data:, performance_data:)
|
12
|
-
@current_status_presenter = QaServer::MonitorStatus::CurrentStatusPresenter.new(current_summary: current_summary, current_failure_data: current_failure_data)
|
13
|
-
@history_presenter = QaServer::MonitorStatus::HistoryPresenter.new(historical_summary_data: historical_summary_data)
|
14
|
-
@performance_presenter = QaServer::MonitorStatus::PerformancePresenter.new(performance_data: performance_data)
|
12
|
+
@current_status_presenter = QaServer::MonitorStatus::CurrentStatusPresenter.new(parent: self, current_summary: current_summary, current_failure_data: current_failure_data)
|
13
|
+
@history_presenter = QaServer::MonitorStatus::HistoryPresenter.new(parent: self, historical_summary_data: historical_summary_data)
|
14
|
+
@performance_presenter = QaServer::MonitorStatus::PerformancePresenter.new(parent: self, performance_data: performance_data)
|
15
15
|
end
|
16
16
|
|
17
|
-
def_delegators :@current_status_presenter, :last_updated, :
|
18
|
-
:authorities_count_style, :tests_count, :passing_tests_count, :failing_tests_count,
|
19
|
-
:failures, :failures?
|
17
|
+
def_delegators :@current_status_presenter, :last_updated_dt, :last_updated, :first_updated_dt, :first_updated, :authorities_count,
|
18
|
+
:failing_authorities_count, :authorities_count_style, :tests_count, :passing_tests_count, :failing_tests_count,
|
19
|
+
:failing_tests_style, :failures, :failures?
|
20
20
|
|
21
21
|
def_delegators :@history_presenter, :historical_summary, :history?, :historical_graph, :status_style_class, :status_label,
|
22
22
|
:historical_data_authority_name, :days_authority_passing, :days_authority_failing, :days_authority_tested,
|
23
23
|
:percent_authority_failing, :percent_authority_failing_str, :failure_style_class, :passing_style_class,
|
24
|
-
:display_history_details?, :display_historical_graph?, :display_historical_datatable
|
24
|
+
:display_history_details?, :display_historical_graph?, :display_historical_datatable?, :history_start, :history_end
|
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,
|
@@ -35,6 +35,6 @@ module QaServer
|
|
35
35
|
:performance_graph_data_section_id, :performance_graph_data_section_base_id, :performance_data_section_class,
|
36
36
|
:performance_day_graph?, :performance_month_graph?, :performance_year_graph?, :performance_all_actions_graph?,
|
37
37
|
:performance_search_graph?, :performance_fetch_graph?, :performance_table_description, :performance_graph_time_period,
|
38
|
-
:performance_graph_action
|
38
|
+
:performance_graph_action, :performance_data_start, :performance_data_end
|
39
39
|
end
|
40
40
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Helper methods for caching for monitoring status.
|
3
|
+
module QaServer
|
4
|
+
class MonitorCacheService
|
5
|
+
class << self
|
6
|
+
# @return [Float] number of seconds until cache should expire
|
7
|
+
def cache_expiry
|
8
|
+
monitoring_expires_at - QaServer::TimeService.current_time
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# @return [ActiveSupport::TimeWithZone] DateTime at which cache should expire
|
14
|
+
def monitoring_expires_at
|
15
|
+
offset = QaServer.config.hour_offset_to_expire_cache
|
16
|
+
offset_time = QaServer::TimeService.current_time
|
17
|
+
offset_time = offset_time.tomorrow unless (offset_time + 5.minutes).hour < offset
|
18
|
+
offset_time.beginning_of_day + offset.hours - 5.minutes
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -14,18 +14,29 @@ module QaServer
|
|
14
14
|
@stats = {}
|
15
15
|
end
|
16
16
|
|
17
|
-
# Calculate performance statistics
|
17
|
+
# Calculate performance statistics with percentiles. Min is at the 10th percentile. Max is at the 90th percentile.
|
18
18
|
# @return [Hash] hash of the statistics
|
19
19
|
# @example
|
20
20
|
# { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
|
21
21
|
# retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
|
22
22
|
# retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 }
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
def calculate_stats_with_percentiles
|
24
|
+
calculate_retrieve_stats(true, true, true)
|
25
|
+
calculate_graph_load_stats(true, true, true)
|
26
|
+
calculate_normalization_stats(true, true, true)
|
27
|
+
calculate_action_stats(true, true, true)
|
28
|
+
stats
|
29
|
+
end
|
30
|
+
|
31
|
+
# Calculate performance statistics including averages only.
|
32
|
+
# @return [Hash] hash of the statistics
|
33
|
+
# @example
|
34
|
+
# { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5 }
|
35
|
+
def calculate_average_stats
|
36
|
+
calculate_load_stats(true, false, false) # used for backward compatibility only
|
37
|
+
calculate_retrieve_stats(true, false, false)
|
38
|
+
calculate_graph_load_stats(true, false, false)
|
39
|
+
calculate_normalization_stats(true, false, false)
|
29
40
|
stats
|
30
41
|
end
|
31
42
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# This class calculates performance stats for the performance datatable.
|
3
|
+
module QaServer
|
4
|
+
class PerformanceDatatableService
|
5
|
+
class << self
|
6
|
+
include QaServer::PerformanceHistoryDataKeys
|
7
|
+
|
8
|
+
class_attribute :stats_calculator_class, :performance_data_class, :authority_list_class
|
9
|
+
self.stats_calculator_class = QaServer::PerformanceCalculatorService
|
10
|
+
self.performance_data_class = QaServer::PerformanceHistory
|
11
|
+
self.authority_list_class = QaServer::AuthorityListerService
|
12
|
+
|
13
|
+
# Summary of performance by action for each authority for the configured time period (e.g. :day, :month, :year, :all).
|
14
|
+
# @param force [Boolean] if true, calculate the stats even if the cache hasn't expired; otherwise, use cache if not expired
|
15
|
+
# @returns [Hash] performance statistics for configured time period by action for each authority
|
16
|
+
# @example
|
17
|
+
# { all_authorities:
|
18
|
+
# { search:
|
19
|
+
# { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
|
20
|
+
# retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
|
21
|
+
# retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 },
|
22
|
+
# fetch: { ... # same data as for search_stats },
|
23
|
+
# all: { ... # same data as for search_stats }
|
24
|
+
# },
|
25
|
+
# AGROVOC_LD4L_CACHE: { ... # same data for each authority }
|
26
|
+
# }
|
27
|
+
def calculate_datatable_data(force:)
|
28
|
+
Rails.cache.fetch("QaServer::PerformanceDatatableService/#{__method__}", expires_in: QaServer::MonitorCacheService.cache_expiry, race_condition_ttl: 5.minutes, force: force) do
|
29
|
+
QaServer.config.monitor_logger.info("(QaServer::PerformanceDatatableService##{__method__}) - calculating performance datatable stats - cache expired or refresh requested (force: #{force})")
|
30
|
+
data = {}
|
31
|
+
auths = authority_list_class.authorities_list
|
32
|
+
data[ALL_AUTH] = datatable_data_for_authority
|
33
|
+
auths.each { |auth_name| data[auth_name] = datatable_data_for_authority(authority_name: auth_name) }
|
34
|
+
data
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def datatable_data_for_authority(authority_name: nil)
|
41
|
+
[:search, :fetch, :all_actions].each_with_object({}) do |action, hash|
|
42
|
+
hash[action] = data_table_stats(authority_name, action)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get statistics for data table.
|
47
|
+
# @param auth_name [String] limit statistics to records for the given authority (default: all authorities)
|
48
|
+
# @param action [Symbol] one of :search, :fetch, :all_actions
|
49
|
+
# @returns [Hash] performance statistics for the datatable during the expected time period
|
50
|
+
# @example
|
51
|
+
# { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
|
52
|
+
# retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
|
53
|
+
# retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 }
|
54
|
+
def data_table_stats(auth_name, action)
|
55
|
+
records = records_for_authority(auth_name)
|
56
|
+
stats_calculator_class.new(records, action: action).calculate_stats_with_percentiles
|
57
|
+
end
|
58
|
+
|
59
|
+
def expected_time_period
|
60
|
+
QaServer.config.performance_datatable_default_time_period
|
61
|
+
end
|
62
|
+
|
63
|
+
def records_for_authority(auth_name)
|
64
|
+
case expected_time_period
|
65
|
+
when :day
|
66
|
+
QaServer.config.performance_cache.write_all # only need to write if just using today's data
|
67
|
+
performance_data_class.where(QaServer::TimePeriodService.where_clause_for_last_24_hours(auth_name: auth_name))
|
68
|
+
when :month
|
69
|
+
performance_data_class.where(QaServer::TimePeriodService.where_clause_for_last_30_days(auth_name: auth_name))
|
70
|
+
when :year
|
71
|
+
performance_data_class.where(QaServer::TimePeriodService.where_clause_for_last_12_months(auth_name: auth_name))
|
72
|
+
else
|
73
|
+
all_records(auth_name)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def all_records(auth_name)
|
78
|
+
auth_name.nil? ? performance_data_class.all : performance_data_class.where(authority: auth_name)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -1,76 +1,133 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# This class sets performance stats for the last 24 hours, past 30 days, and the past 12 months.
|
3
3
|
module QaServer
|
4
|
-
class PerformanceGraphDataService
|
4
|
+
class PerformanceGraphDataService # rubocop:disable Metrics/ClassLength
|
5
5
|
class << self
|
6
6
|
include QaServer::PerformanceHistoryDataKeys
|
7
7
|
|
8
|
-
class_attribute :stats_calculator_class, :performance_data_class
|
8
|
+
class_attribute :stats_calculator_class, :performance_data_class, :authority_list_class
|
9
9
|
self.stats_calculator_class = QaServer::PerformanceCalculatorService
|
10
10
|
self.performance_data_class = QaServer::PerformanceHistory
|
11
|
+
self.authority_list_class = QaServer::AuthorityListerService
|
11
12
|
|
12
|
-
#
|
13
|
-
# @param
|
14
|
-
# @param action [Symbol] one of :search, :fetch, :all_actions
|
15
|
-
# @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
|
13
|
+
# Performance data for a day, a month, a year, and all time for each authority.
|
14
|
+
# @param datatype [Symbol] what type of data should be calculated (e.g. :datatable, :graph, :all)
|
16
15
|
# @returns [Hash] performance statistics for the past 24 hours
|
17
16
|
# @example
|
18
|
-
# {
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
17
|
+
# { all_authorities:
|
18
|
+
# { search:
|
19
|
+
# {
|
20
|
+
# day:
|
21
|
+
# { 0: { hour: '1400', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
22
|
+
# 1: { hour: '1500', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
23
|
+
# 2: { hour: '1600', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
24
|
+
# ...,
|
25
|
+
# 23: { hour: 'NOW', retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
26
|
+
# },
|
27
|
+
# month:
|
28
|
+
# { 0: { day: '07-15-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
29
|
+
# 1: { day: '07-16-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
30
|
+
# 2: { day: '07-17-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
31
|
+
# ...,
|
32
|
+
# 29: { day: 'TODAY', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
33
|
+
# },
|
34
|
+
# year:
|
35
|
+
# { 0: { month: '09-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
36
|
+
# 1: { month: '10-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
37
|
+
# 2: { month: '11-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
38
|
+
# ...,
|
39
|
+
# 11: { month: '08-2019', stats: { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
40
|
+
# }
|
41
|
+
# },
|
42
|
+
# fetch: { ... # same data as for search_stats },
|
43
|
+
# all: { ... # same data as for search_stats }
|
44
|
+
# },
|
45
|
+
# AGROVOC_LD4L_CACHE: { ... # same data for each authority }
|
23
46
|
# }
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
47
|
+
def calculate_graph_data(force:)
|
48
|
+
QaServer.config.monitor_logger.info("(QaServer::PerformanceGraphDataService##{__method__}) - calculating performance graph data")
|
49
|
+
QaServer.config.performance_cache.write_all
|
50
|
+
data = {}
|
51
|
+
auths = authority_list_class.authorities_list
|
52
|
+
calculate_all = force || cache_expired?
|
53
|
+
data[ALL_AUTH] = graph_data_for_authority(force: force, calculate_all: calculate_all)
|
54
|
+
auths.each { |auth_name| data[auth_name] = graph_data_for_authority(authority_name: auth_name, force: force, calculate_all: calculate_all) }
|
55
|
+
data
|
31
56
|
end
|
32
57
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# 29: { day: 'TODAY', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
44
|
-
# }
|
45
|
-
def average_last_30_days(authority_name: nil, action: nil, force: false)
|
46
|
-
Rails.cache.fetch("#{self.class}/#{__method__}/#{authority_name || ALL_AUTH}/#{action}/#{FOR_MONTH}",
|
47
|
-
expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: force) do
|
48
|
-
Rails.logger.info("#{self.class}##{__method__} - calculating performance stats for last 30 days - cache expired or refresh requested (#{force})")
|
49
|
-
calculate_last_30_days(authority_name, action)
|
58
|
+
private
|
59
|
+
|
60
|
+
def graph_data_for_authority(authority_name: nil, force:, calculate_all:)
|
61
|
+
[:search, :fetch, :all_actions].each_with_object({}) do |action, hash|
|
62
|
+
data = {}
|
63
|
+
data[FOR_DAY] = average_last_24_hours(authority_name: authority_name, action: action, force: force)
|
64
|
+
data[FOR_MONTH] = average_last_30_days(authority_name: authority_name, action: action, force: force) if calculate_all
|
65
|
+
data[FOR_YEAR] = average_last_12_months(authority_name: authority_name, action: action, force: force) if calculate_all
|
66
|
+
hash[action] = data
|
67
|
+
end
|
50
68
|
end
|
51
|
-
end
|
52
69
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
+
# Get hourly average for the past 24 hours.
|
71
|
+
# @param authority_name [String] limit statistics to records for the given authority (default: all authorities)
|
72
|
+
# @param action [Symbol] one of :search, :fetch, :all_actions
|
73
|
+
# @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
|
74
|
+
# @returns [Hash] performance statistics for the past 24 hours
|
75
|
+
# @example
|
76
|
+
# { 0: { hour: '1400', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
77
|
+
# 1: { hour: '1500', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
78
|
+
# 2: { hour: '1600', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
79
|
+
# ...,
|
80
|
+
# 23: { hour: 'NOW', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
81
|
+
# }
|
82
|
+
def average_last_24_hours(authority_name: nil, action: nil, force: false)
|
83
|
+
avgs = Rails.cache.fetch("QaServer::PerformanceGraphDataService/#{__method__}/#{authority_name || ALL_AUTH}/#{action}/#{FOR_DAY}",
|
84
|
+
expires_in: QaServer::TimeService.current_time.end_of_hour - QaServer::TimeService.current_time,
|
85
|
+
race_condition_ttl: 1.hour, force: force) do
|
86
|
+
QaServer.config.monitor_logger.info("(QaServer::PerformanceGraphDataService##{__method__}) - calculating performance stats - cache expired or refresh requested (force: #{force})")
|
87
|
+
calculate_last_24_hours(authority_name, action)
|
88
|
+
end
|
89
|
+
calculate_last_hour(authority_name, action, avgs)
|
70
90
|
end
|
71
|
-
end
|
72
91
|
|
73
|
-
|
92
|
+
# Get daily average for the past 30 days.
|
93
|
+
# @param authority_name [String] limit statistics to records for the given authority (default: all authorities)
|
94
|
+
# @param action [Symbol] one of :search, :fetch, :all_actions
|
95
|
+
# @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
|
96
|
+
# @returns [Hash] performance statistics for the past 30 days
|
97
|
+
# @example
|
98
|
+
# { 0: { day: '07-15-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
99
|
+
# 1: { day: '07-16-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
100
|
+
# 2: { day: '07-17-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
101
|
+
# ...,
|
102
|
+
# 29: { day: 'TODAY', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
103
|
+
# }
|
104
|
+
def average_last_30_days(authority_name: nil, action: nil, force: false)
|
105
|
+
Rails.cache.fetch("QaServer::PerformanceGraphDataService/#{__method__}/#{authority_name || ALL_AUTH}/#{action}/#{FOR_MONTH}",
|
106
|
+
expires_in: QaServer::MonitorCacheService.cache_expiry, race_condition_ttl: 1.hour, force: force) do
|
107
|
+
QaServer.config.monitor_logger.info("(QaServer::PerformanceGraphDataService##{__method__}) - calculating performance stats - cache expired or refresh requested (force: #{force})")
|
108
|
+
calculate_last_30_days(authority_name, action)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get daily average for the past 12 months.
|
113
|
+
# @param authority_name [String] limit statistics to records for the given authority (default: all authorities)
|
114
|
+
# @param action [Symbol] one of :search, :fetch, :all_actions
|
115
|
+
# @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
|
116
|
+
# @returns [Hash] performance statistics for the past 12 months
|
117
|
+
# @example
|
118
|
+
# { 0: { month: '09-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
119
|
+
# 1: { month: '10-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
120
|
+
# 2: { month: '11-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
121
|
+
# ...,
|
122
|
+
# 11: { month: '08-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
123
|
+
# }
|
124
|
+
def average_last_12_months(authority_name: nil, action: nil, force: false)
|
125
|
+
Rails.cache.fetch("QaServer::PerformanceGraphDataService/#{__method__}/#{authority_name || ALL_AUTH}/#{action}/#{FOR_YEAR}",
|
126
|
+
expires_in: QaServer::MonitorCacheService.cache_expiry, race_condition_ttl: 1.hour, force: force) do
|
127
|
+
QaServer.config.monitor_logger.info("(QaServer::PerformanceGraphDataService##{__method__}) - calculating performance stats - cache expired or refresh requested (force: #{force})")
|
128
|
+
calculate_last_12_months(authority_name, action)
|
129
|
+
end
|
130
|
+
end
|
74
131
|
|
75
132
|
def records_by(authority_name, action, time_period)
|
76
133
|
where_clause = { dt_stamp: time_period }
|
@@ -99,49 +156,50 @@ module QaServer
|
|
99
156
|
end
|
100
157
|
end
|
101
158
|
|
159
|
+
def calculate_from_records(records, range_idx, range_label)
|
160
|
+
stats = stats_calculator_class.new(records).calculate_average_stats
|
161
|
+
{ STATS => stats, range_idx => range_label }
|
162
|
+
end
|
163
|
+
|
164
|
+
def calculate_last_hour(authority_name, action, avgs)
|
165
|
+
start_hour = QaServer::TimeService.current_time.beginning_of_hour
|
166
|
+
records = records_by(authority_name, action, start_hour..start_hour.end_of_hour)
|
167
|
+
avgs[23] = calculate_from_records(records, BY_HOUR, performance_by_hour_label(23, start_hour))
|
168
|
+
avgs
|
169
|
+
end
|
170
|
+
|
102
171
|
def calculate_last_24_hours(authority_name, action)
|
103
|
-
start_hour = QaServer.current_time.beginning_of_hour - 23.hours
|
104
|
-
|
105
|
-
0.upto(23).each do |idx|
|
172
|
+
start_hour = QaServer::TimeService.current_time.beginning_of_hour - 23.hours
|
173
|
+
0.upto(23).each_with_object({}) do |idx, avgs|
|
106
174
|
records = records_by(authority_name, action, start_hour..start_hour.end_of_hour)
|
107
|
-
|
108
|
-
data = {}
|
109
|
-
data[BY_HOUR] = performance_by_hour_label(idx, start_hour)
|
110
|
-
data[STATS] = stats
|
111
|
-
avgs[idx] = data
|
175
|
+
avgs[idx] = calculate_from_records(records, BY_HOUR, performance_by_hour_label(idx, start_hour))
|
112
176
|
start_hour += 1.hour
|
113
177
|
end
|
114
|
-
avgs
|
115
178
|
end
|
116
179
|
|
117
180
|
def calculate_last_30_days(authority_name, action)
|
118
|
-
start_day = QaServer.current_time.beginning_of_day - 29.days
|
119
|
-
|
120
|
-
0.upto(29).each do |idx|
|
181
|
+
start_day = QaServer::TimeService.current_time.beginning_of_day - 29.days
|
182
|
+
0.upto(29).each_with_object({}) do |idx, avgs|
|
121
183
|
records = records_by(authority_name, action, start_day..start_day.end_of_day)
|
122
|
-
|
123
|
-
data = {}
|
124
|
-
data[BY_DAY] = performance_by_day_label(idx, start_day)
|
125
|
-
data[STATS] = stats
|
126
|
-
avgs[idx] = data
|
184
|
+
avgs[idx] = calculate_from_records(records, BY_DAY, performance_by_day_label(idx, start_day))
|
127
185
|
start_day += 1.day
|
128
186
|
end
|
129
|
-
avgs
|
130
187
|
end
|
131
188
|
|
132
189
|
def calculate_last_12_months(authority_name, action)
|
133
|
-
start_month = QaServer.current_time.beginning_of_month - 11.months
|
134
|
-
|
135
|
-
0.upto(11).each do |idx|
|
190
|
+
start_month = QaServer::TimeService.current_time.beginning_of_month - 11.months
|
191
|
+
0.upto(11).each_with_object({}) do |idx, avgs|
|
136
192
|
records = records_by(authority_name, action, start_month..start_month.end_of_month)
|
137
|
-
|
138
|
-
data = {}
|
139
|
-
data[BY_MONTH] = start_month.strftime("%m-%Y")
|
140
|
-
data[STATS] = stats
|
141
|
-
avgs[idx] = data
|
193
|
+
avgs[idx] = calculate_from_records(records, BY_MONTH, start_month.strftime("%m-%Y"))
|
142
194
|
start_month += 1.month
|
143
195
|
end
|
144
|
-
|
196
|
+
end
|
197
|
+
|
198
|
+
# @returns [Boolean] true if cache has expired; otherwise, false
|
199
|
+
def cache_expired?
|
200
|
+
expired = Rails.cache.fetch("QaServer::PerformanceGraphDataService/#{__method__}", expires_in: 5.seconds) { true }
|
201
|
+
Rails.cache.fetch("QaServer::PerformanceGraphDataService/#{__method__}", expires_in: QaServer::MonitorCacheService.cache_expiry, force: expired) { false } # reset if expired
|
202
|
+
expired
|
145
203
|
end
|
146
204
|
end
|
147
205
|
end
|