qa_server 5.5.1 → 6.0.0
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 +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
|