qa_server 5.4.0 → 5.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab89fc6147c1cf32de828a8b2cecaab3ca66b296
4
- data.tar.gz: 027160d4a1e0f0090a1c6d81cfdcd74fba5d1423
3
+ metadata.gz: cfe60b29f718a16c9c86b62e0bd7807c746147b1
4
+ data.tar.gz: 51dc3758debb4b924b7859ca82f9ed49eb3475a6
5
5
  SHA512:
6
- metadata.gz: aa48a9de27b5b612d590981dfbb9f4316f496a6ecee810bc25735f7ffd1d5fc73c4a6e9f63af65aaeb767a35d983a9c3680ab1cd27b5558ef7b4c11958d73efe
7
- data.tar.gz: 12c86bbeb3d41de071788f93fdfd3ef632a1300c77182a114124a5fd5895ac733e0c4f76b72e973a3cd7b5a691224b2fb7be21b2651e04a0a935ad283ca8a973
6
+ metadata.gz: 56e1811a7ee5b562a3e24d77fecba594955b5410824863a0745f895b2541d5d735e21c33b29b33047c4cb1d257489dc498d9bdb774f097a74c64954c2c7e0da8
7
+ data.tar.gz: '084ba6a28a84a2a235c2cb69f2aed5285399ba2e3718135302a5dbf3c297a225407a348cbf6b87ef1705b5cc59016b1bd3ae49a7b0947dda28c215961f9e0f9a'
@@ -22,3 +22,7 @@ Metrics/BlockLength:
22
22
  Exclude:
23
23
  - 'qa_server.gemspec'
24
24
  - 'spec/**/*.rb'
25
+
26
+ Metrics/ClassLength:
27
+ Exclude:
28
+ - 'lib/qa_server/configuration.rb'
@@ -1,3 +1,7 @@
1
+ ### 5.5.0 (2020-01-10)
2
+
3
+ * use caching with expiry for monitor status page
4
+
1
5
  ### 5.4.0 (2020-01-07)
2
6
 
3
7
  * adds config hour_offset_to_expire_cache
@@ -13,9 +13,7 @@ module QaServer
13
13
 
14
14
  # Sets up presenter with data to display in the UI
15
15
  def index
16
- refresh_tests
17
- historical_data = refresh_history
18
- performance_data = refresh_performance
16
+ latest_run
19
17
  @presenter = presenter_class.new(current_summary: latest_summary,
20
18
  current_failure_data: latest_failures,
21
19
  historical_summary_data: historical_data,
@@ -25,49 +23,39 @@ module QaServer
25
23
 
26
24
  private
27
25
 
26
+ # Sets @latest_run [QaServer::ScenarioRunRegistry]
28
27
  def latest_run
29
- @latest_run ||= scenario_run_registry_class.latest_run
28
+ Rails.cache.fetch("#{self.class}/#{__method__}", expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: refresh_tests?) do
29
+ Rails.logger.info("#{self.class}##{__method__} - Running Tests - cache expired or refresh requested (#{refresh_tests?})")
30
+ validate(authorities_list)
31
+ scenario_run_registry_class.save_run(scenarios_results: status_log.to_a)
32
+ scenario_run_registry_class.latest_run
33
+ end
30
34
  end
31
35
 
36
+ # Sets @latest_summary [QaServer::ScenarioRunSummary]
32
37
  def latest_summary
33
- @latest_summary ||= scenario_history_class.run_summary(scenario_run: latest_run)
38
+ scenario_history_class.run_summary(scenario_run: latest_run, force: refresh_tests?)
34
39
  end
35
40
 
36
41
  def latest_failures
37
- @status_data ||= scenario_history_class.run_failures(run_id: latest_run.id)
38
- end
39
-
40
- def update_summary_and_data
41
- scenario_run_registry_class.save_run(scenarios_results: status_log.to_a)
42
- @latest_summary = nil # reset so next request recalculates
43
- @latest_failures = nil # reset so next request recalculates
42
+ scenario_history_class.run_failures(run_id: latest_run.id, force: refresh_tests?)
44
43
  end
45
44
 
46
- def expired?
47
- @expired ||= latest_summary.blank? || latest_summary.run_dt_stamp < QaServer.monitoring_expires_at
48
- end
49
-
50
- def historical_summary_data(refresh: false)
51
- # TODO: Make this refresh the same way performance data refreshes.
52
- # Requires historical graph to move out of presenter so it can be created here only with refresh.
53
- if refresh
54
- @historical_summary_data = scenario_history_class.historical_summary
55
- # TODO: Need to recreate graph here. And need to only read the graph in presenter.
56
- end
57
- @historical_summary_data ||= scenario_history_class.historical_summary
45
+ # Sets @historical_data [Array<Hash>]
46
+ def historical_data
47
+ scenario_history_class.historical_summary(force: refresh_history?)
58
48
  end
59
49
 
60
- def performance_data(refresh: false)
61
- datatype = performance_datatype(refresh)
62
- return if datatype == :none
63
- @performance_data = nil if refresh
64
- @performance_data ||= performance_history_class.performance_data(datatype: datatype)
50
+ # Sets @performance_data [Hash<Hash>]
51
+ def performance_data
52
+ performance_history_class.performance_data(datatype: performance_datatype, force: refresh_performance?)
65
53
  end
66
54
 
67
- def performance_datatype(refresh) # rubocop:disable Metrics/CyclomaticComplexity
68
- return :all if display_performance_datatable? && display_performance_graph? && refresh
55
+ def performance_datatype
56
+ return :all if display_performance_datatable? && display_performance_graph?
69
57
  return :datatable if display_performance_datatable?
70
- return :graph if display_performance_graph? && refresh
58
+ return :graph if display_performance_graph?
71
59
  :none
72
60
  end
73
61
 
@@ -79,12 +67,6 @@ module QaServer
79
67
  @display_performance_graph ||= QaServer.config.display_performance_graph?
80
68
  end
81
69
 
82
- def refresh_tests
83
- return unless refresh_tests?
84
- validate(authorities_list)
85
- update_summary_and_data
86
- end
87
-
88
70
  def refresh_history
89
71
  historical_summary_data(refresh: refresh_history?)
90
72
  end
@@ -98,12 +80,13 @@ module QaServer
98
80
  end
99
81
 
100
82
  def refresh_all?
83
+ return false unless refresh?
101
84
  params[:refresh].nil? || params[:refresh].casecmp?('all') # nil is for backward compatibility
102
85
  end
103
86
 
104
87
  def refresh_tests?
105
- return false unless refresh? || expired?
106
- refresh_all? || params[:refresh].casecmp?('tests') || expired?
88
+ return false unless refresh?
89
+ refresh_all? || params[:refresh].casecmp?('tests')
107
90
  end
108
91
 
109
92
  def refresh_history?
@@ -112,8 +95,8 @@ module QaServer
112
95
  end
113
96
 
114
97
  def refresh_performance?
115
- return false unless refresh? || expired?
116
- refresh_all? || params[:refresh].casecmp?('performance') || expired?
98
+ return false unless refresh?
99
+ refresh_all? || params[:refresh].casecmp?('performance')
117
100
  end
118
101
  end
119
102
  end
@@ -41,6 +41,7 @@ module QaServer
41
41
  end
42
42
 
43
43
  def log(id:)
44
+ return if QaServer.config.suppress_logging_performance_datails
44
45
  Rails.logger.debug("*** performance data for id: #{id} ***")
45
46
  Rails.logger.debug(@cache[id].to_yaml)
46
47
  end
@@ -64,10 +64,10 @@ module QaServer
64
64
  # },
65
65
  # AGROVOC_LD4L_CACHE: { ... # same data for each authority }
66
66
  # }
67
- def performance_data(datatype: :datatable)
67
+ def performance_data(datatype: :datatable, force: false)
68
68
  return if datatype == :none
69
69
  QaServer.config.performance_cache.write_all
70
- data = calculate_data(datatype)
70
+ data = calculate_data(datatype, force: force)
71
71
  graphing_service_class.create_performance_graphs(performance_data: data) if calculate_graphdata? datatype
72
72
  data
73
73
  end
@@ -82,23 +82,23 @@ module QaServer
82
82
  datatype == :graph || datatype == :all
83
83
  end
84
84
 
85
- def calculate_data(datatype)
85
+ def calculate_data(datatype, force:)
86
86
  data = {}
87
87
  auths = authority_list_class.authorities_list
88
- data[ALL_AUTH] = data_for_authority(datatype: datatype)
89
- auths.each { |auth_name| data[auth_name] = data_for_authority(authority_name: auth_name, datatype: datatype) }
88
+ data[ALL_AUTH] = data_for_authority(datatype: datatype, force: force)
89
+ auths.each { |auth_name| data[auth_name] = data_for_authority(authority_name: auth_name, datatype: datatype, force: force) }
90
90
  data
91
91
  end
92
92
 
93
- def data_for_authority(authority_name: nil, datatype:)
93
+ def data_for_authority(authority_name: nil, datatype:, force:)
94
94
  action_data = {}
95
95
  [:search, :fetch, :all_actions].each do |action|
96
96
  data = {}
97
- data[FOR_DATATABLE] = data_table_stats(authority_name, action) if calculate_datatable?(datatype)
97
+ data[FOR_DATATABLE] = data_table_stats(authority_name, action, force: force) if calculate_datatable?(datatype)
98
98
  if calculate_graphdata?(datatype)
99
- data[FOR_DAY] = graph_data_service_class.average_last_24_hours(authority_name: authority_name, action: action)
100
- data[FOR_MONTH] = graph_data_service_class.average_last_30_days(authority_name: authority_name, action: action)
101
- data[FOR_YEAR] = graph_data_service_class.average_last_12_months(authority_name: authority_name, action: action)
99
+ data[FOR_DAY] = graph_data_service_class.average_last_24_hours(authority_name: authority_name, action: action, force: force)
100
+ data[FOR_MONTH] = graph_data_service_class.average_last_30_days(authority_name: authority_name, action: action, force: force)
101
+ data[FOR_YEAR] = graph_data_service_class.average_last_12_months(authority_name: authority_name, action: action, force: force)
102
102
  end
103
103
  action_data[action] = data
104
104
  end
@@ -106,18 +106,23 @@ module QaServer
106
106
  end
107
107
 
108
108
  # Get statistics for all available data.
109
- # @param [String] auth_name - limit statistics to records for the given authority (default: all authorities)
109
+ # @param auth_name [String] limit statistics to records for the given authority (default: all authorities)
110
+ # @param action [Symbol] one of :search, :fetch, :all_actions
111
+ # @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
110
112
  # @returns [Hash] performance statistics for the datatable during the expected time period
111
113
  # @example
112
114
  # { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
113
115
  # retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
114
116
  # retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 }
115
- def data_table_stats(auth_name, action)
116
- records = records_for_last_24_hours(auth_name) ||
117
- records_for_last_30_days(auth_name) ||
118
- records_for_last_12_months(auth_name) ||
119
- all_records(auth_name)
120
- stats_calculator_class.new(records, action: action).calculate_stats(avg: true, low: true, high: true)
117
+ def data_table_stats(auth_name, action, force:)
118
+ Rails.cache.fetch("#{self.class}/#{__method__}/#{auth_name || ALL_AUTH}/#{action}/#{FOR_DATATABLE}", expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: force) do
119
+ Rails.logger.info("#{self.class}##{__method__} - calculating performance datatable stats - cache expired or refresh requested (#{force})")
120
+ records = records_for_last_24_hours(auth_name) ||
121
+ records_for_last_30_days(auth_name) ||
122
+ records_for_last_12_months(auth_name) ||
123
+ all_records(auth_name)
124
+ stats_calculator_class.new(records, action: action).calculate_stats(avg: true, low: true, high: true)
125
+ end
121
126
  end
122
127
 
123
128
  def expected_time_period
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  # Provide access to the scenario_results_history database table which tracks specific scenario runs over time.
3
3
  module QaServer
4
- class ScenarioRunHistory < ActiveRecord::Base
4
+ class ScenarioRunHistory < ActiveRecord::Base # rubocop:disable Metrics/ClassLength
5
5
  self.table_name = 'scenario_run_history'
6
6
  belongs_to :scenario_run_registry
7
7
  enum scenario_type: [:connection, :accuracy, :performance], _suffix: :type
@@ -31,24 +31,28 @@ module QaServer
31
31
  end
32
32
 
33
33
  # Get a summary of passing/failing tests for a run.
34
- # @param scenario_run [ScenarioRunRegistry] the run on which to gather statistics
35
- # @returns [Hash] statistics on the requested run
36
- # @example
37
- # { run_id: 14,
38
- # failing_count: 3,
39
- # passing_count: 156,
40
- # total_count: 159,
41
- # authority_count: 22,
42
- # failing_authority_count: 1 }
43
- def self.run_summary(scenario_run:)
44
- return nil unless scenario_run&.id
45
- status = status_counts_in_run(run_id: scenario_run.id)
46
- summary_class.new(run_id: scenario_run.id,
47
- run_dt_stamp: scenario_run.dt_stamp,
48
- authority_count: authorities_in_run(run_id: scenario_run.id).count,
49
- failing_authority_count: authorities_with_failures_in_run(run_id: scenario_run.id).count,
50
- passing_scenario_count: status['good'],
51
- failing_scenario_count: status['bad'] + status['unknown'])
34
+ # @param scenario_run [QaServer::ScenarioRunRegistry] the run on which to gather statistics
35
+ # @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
36
+ # @returns [QaServer::ScenarioRunSummary] statistics on the requested run
37
+ # @example ScenarioRunSummary includes methods for accessing
38
+ # * run_id: 14,
39
+ # * run_dt_stamp:
40
+ # * authority_count: 22,
41
+ # * failing_authority_count: 1
42
+ # * passing_scenario_count: 156,
43
+ # * failing_scenario_count: 3,
44
+ # * total_scenario_count: 159,
45
+ def self.run_summary(scenario_run:, force: false)
46
+ Rails.cache.fetch("#{self.class}/#{__method__}", expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: force) do
47
+ Rails.logger.info("#{self.class}##{__method__} - creating summary of latest run - cache expired or refresh requested (#{force})")
48
+ status = status_counts_in_run(run_id: scenario_run.id)
49
+ summary_class.new(run_id: scenario_run.id,
50
+ run_dt_stamp: scenario_run.dt_stamp,
51
+ authority_count: authorities_in_run(run_id: scenario_run.id).count,
52
+ failing_authority_count: authorities_with_failures_in_run(run_id: scenario_run.id).count,
53
+ passing_scenario_count: status['good'],
54
+ failing_scenario_count: status['bad'] + status['unknown'])
55
+ end
52
56
  end
53
57
 
54
58
  # Get set of all scenario results for a run.
@@ -85,6 +89,7 @@ module QaServer
85
89
  # err_message: "Not enough search results returned",
86
90
  # scenario_type: :connection
87
91
  # run_time: 0.123 } ]
92
+ # @deprecated Not used anywhere. Being removed.
88
93
  def self.run_results(run_id:, authority_name: nil, status: nil, url: nil)
89
94
  return [] unless run_id
90
95
  where = {}
@@ -94,9 +99,11 @@ module QaServer
94
99
  where[:url] = url if url.present?
95
100
  QaServer::ScenarioRunHistory.where(where).to_a
96
101
  end
102
+ deprecation_deprecate run_results: "Not used anywhere. Being removed."
97
103
 
98
104
  # Get set of failures for a run, if any.
99
105
  # @param run_id [Integer] the run on which to gather statistics
106
+ # @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
100
107
  # @returns [Array<Hash>] scenario details for any failing scenarios in the run
101
108
  # @example
102
109
  # [ { status: :bad,
@@ -117,32 +124,43 @@ module QaServer
117
124
  # err_message: "Not enough search results returned",
118
125
  # scenario_type: :connection
119
126
  # run_time: 0.123 } ]
120
- def self.run_failures(run_id:)
127
+ def self.run_failures(run_id:, force: false)
121
128
  return [] unless run_id
122
- QaServer::ScenarioRunHistory.where(scenario_run_registry_id: run_id).where.not(status: :good).to_a
129
+ Rails.cache.fetch("#{self.class}/#{__method__}", expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: force) do
130
+ Rails.logger.info("#{self.class}##{__method__} - finding failures in latest run - cache expired or refresh requested (#{force})")
131
+ QaServer::ScenarioRunHistory.where(scenario_run_registry_id: run_id).where.not(status: :good).to_a
132
+ end
123
133
  end
124
134
 
125
135
  # Get a summary level of historical data
126
- # @returns [Array<Hash>] scenario details for any failing scenarios in the run (auth_name, failing, passing)
127
- # @example
136
+ # @returns [Array<Array>] summary of passing/failing tests for each authority
137
+ # @example [auth_name, failing, passing]
128
138
  # [ [ 'agrovoc', 0, 24 ],
129
139
  # [ 'geonames_ld4l_cache', 2, 22 ] ... ]
130
- def self.historical_summary
131
- runs = all_runs_per_authority
132
- failures = failing_runs_per_authority
133
- return [] unless runs.present?
134
- data = []
135
- runs.each do |auth_name, run_count|
136
- auth_data = []
137
- auth_data[0] = auth_name
138
- failure_count = (failures.key? auth_name) ? failures[auth_name] : 0 # rubocop:disable Style/TernaryParentheses
139
- auth_data[1] = failure_count
140
- auth_data[2] = run_count - failure_count # passing
141
- data << auth_data
140
+ def self.historical_summary(force: false)
141
+ Rails.cache.fetch("#{self.class}/#{__method__}", expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: force) do
142
+ Rails.logger.info("#{self.class}##{__method__} - calculating pass/fail per authority across all time - cache expired or refresh requested (#{force})")
143
+ runs = all_runs_per_authority
144
+ failures = failing_runs_per_authority
145
+ return [] unless runs.present?
146
+ data = []
147
+ runs.each do |auth_name, run_count|
148
+ data << pass_fail_stats_for_authority(failures, auth_name, run_count)
149
+ end
150
+ data
142
151
  end
143
- data
144
152
  end
145
153
 
154
+ def self.pass_fail_stats_for_authority(failures, auth_name, run_count)
155
+ auth_data = []
156
+ auth_data[0] = auth_name
157
+ failure_count = failures.key?(auth_name) ? failures[auth_name] : 0
158
+ auth_data[1] = failure_count
159
+ auth_data[2] = run_count - failure_count # passing
160
+ auth_data
161
+ end
162
+ private_class_method :pass_fail_stats_for_authority
163
+
146
164
  def self.authorities_in_run(run_id:)
147
165
  QaServer::ScenarioRunHistory.where(scenario_run_registry_id: run_id).pluck(:authority_name).uniq
148
166
  end
@@ -153,6 +171,8 @@ module QaServer
153
171
  end
154
172
  private_class_method :authorities_with_failures_in_run
155
173
 
174
+ # @return [Hash] status counts across all authorities (used for current test summary)
175
+ # @example { "good" => 23, "bad" => 3, "unknown" => 0 }
156
176
  def self.status_counts_in_run(run_id:)
157
177
  status = QaServer::ScenarioRunHistory.group('status').where(scenario_run_registry_id: run_id).count
158
178
  status["good"] = 0 unless status.key? "good"
@@ -5,7 +5,7 @@ module QaServer
5
5
  self.table_name = 'scenario_run_registry'
6
6
  has_many :scenario_run_history, foreign_key: :scenario_run_registry_id
7
7
 
8
- # Get the latest saved run of scenarios.
8
+ # @return [ScenarioRunRegistry] registry data for latest run (e.g. id, dt_stamp)
9
9
  def self.latest_run
10
10
  return nil unless QaServer::ScenarioRunRegistry.last
11
11
  QaServer::ScenarioRunRegistry.last # Can we count on last to always be the one with the latest dt_stamp?
@@ -14,21 +14,27 @@ module QaServer
14
14
  # latest_run.id
15
15
  end
16
16
 
17
- # Get the latest saved status.
17
+ # @return [Integer] id for latest test run
18
+ # @deprecated Not used anywhere. Being removed.
18
19
  def self.latest_run_id
19
20
  latest = latest_run
20
21
  return nil unless latest
21
22
  lastest.id
22
23
  end
24
+ deprecation_deprecate latest_run_id: "Not used anywhere. Being removed."
23
25
 
24
- def self.save_run(scenarios_results:)
25
- run = QaServer::ScenarioRunRegistry.create(dt_stamp: dt_stamp_now_et)
26
- scenarios_results.each { |result| QaServer::ScenarioRunHistory.save_result(run_id: run.id, scenario_result: result) }
26
+ # @return [String] datetime stamp of first registered run
27
+ def self.first_run_dt
28
+ Rails.cache.fetch("#{self.class}/#{__method__}", expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour) do
29
+ QaServer::ScenarioRunRegistry.first.dt_stamp.in_time_zone("Eastern Time (US & Canada)").strftime("%m/%d/%y - %I:%M %p")
30
+ end
27
31
  end
28
32
 
29
- def self.dt_stamp_now_et
30
- Time.now.in_time_zone("Eastern Time (US & Canada)")
33
+ # Register and save latest test run results
34
+ # @param scenarios_results [Array<Hash>] results of latest test run
35
+ def self.save_run(scenarios_results:)
36
+ run = QaServer::ScenarioRunRegistry.create(dt_stamp: QaServer.current_time)
37
+ scenarios_results.each { |result| QaServer::ScenarioRunHistory.save_result(run_id: run.id, scenario_result: result) }
31
38
  end
32
- private_class_method :dt_stamp_now_et
33
39
  end
34
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # Abstract class that parses the authority configuration from the yml file into the parts needed by inheriting scenario types.
2
+ # Holds test run summary data providing methods for accessing each data point.
3
3
  module QaServer
4
4
  class ScenarioRunSummary
5
5
  # @return [Integer] the id of the scenario run being summarized
@@ -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 = Time.now.to_f
20
+ start_time_s = QaServer.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 = Time.now.to_f
24
+ end_time_s = QaServer.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 = Time.now.to_f
28
+ start_time_s = QaServer.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 = Time.now.to_f
43
+ end_time_s = QaServer.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
@@ -16,7 +16,7 @@ module QaServer::MonitorStatus
16
16
 
17
17
  # @return [String] date of first recorded test run
18
18
  def first_updated
19
- QaServer::ScenarioRunRegistry.first.dt_stamp.in_time_zone("Eastern Time (US & Canada)").strftime("%m/%d/%y - %I:%M %p")
19
+ QaServer::ScenarioRunRegistry.first_run_dt
20
20
  end
21
21
 
22
22
  # @return [Integer] number of loaded authorities
@@ -10,6 +10,9 @@ module QaServer
10
10
  self.performance_data_class = QaServer::PerformanceHistory
11
11
 
12
12
  # Get hourly average for the past 24 hours.
13
+ # @param authority_name [String] limit statistics to records for the given authority (default: all authorities)
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
16
  # @returns [Hash] performance statistics for the past 24 hours
14
17
  # @example
15
18
  # { 0: { hour: '1400', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
@@ -18,22 +21,19 @@ module QaServer
18
21
  # ...,
19
22
  # 23: { hour: 'NOW', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
20
23
  # }
21
- def average_last_24_hours(authority_name: nil, action: nil)
22
- start_hour = Time.now.getlocal.beginning_of_hour - 23.hours
23
- avgs = {}
24
- 0.upto(23).each do |idx|
25
- records = records_by(authority_name, action, start_hour..start_hour.end_of_hour)
26
- stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
27
- data = {}
28
- data[BY_HOUR] = performance_by_hour_label(idx, start_hour)
29
- data[STATS] = stats
30
- avgs[idx] = data
31
- start_hour += 1.hour
24
+ def average_last_24_hours(authority_name: nil, action: nil, force: false)
25
+ Rails.cache.fetch("#{self.class}/#{__method__}/#{authority_name || ALL_AUTH}/#{action}/#{FOR_DAY}",
26
+ expires_in: QaServer.current_time.end_of_hour - QaServer.current_time,
27
+ race_condition_ttl: 1.hour, force: force) do
28
+ Rails.logger.info("#{self.class}##{__method__} - calculating performance stats for last 24 hours - cache expired or refresh requested (#{force})")
29
+ calculate_last_24_hours(authority_name, action)
32
30
  end
33
- avgs
34
31
  end
35
32
 
36
33
  # Get daily average for the past 30 days.
34
+ # @param authority_name [String] limit statistics to records for the given authority (default: all authorities)
35
+ # @param action [Symbol] one of :search, :fetch, :all_actions
36
+ # @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
37
37
  # @returns [Hash] performance statistics for the past 30 days
38
38
  # @example
39
39
  # { 0: { day: '07-15-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
@@ -42,22 +42,18 @@ module QaServer
42
42
  # ...,
43
43
  # 29: { day: 'TODAY', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
44
44
  # }
45
- def average_last_30_days(authority_name: nil, action: nil)
46
- start_day = Time.now.getlocal.beginning_of_day - 29.days
47
- avgs = {}
48
- 0.upto(29).each do |idx|
49
- records = records_by(authority_name, action, start_day..start_day.end_of_day)
50
- stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
51
- data = {}
52
- data[BY_DAY] = performance_by_day_label(idx, start_day)
53
- data[STATS] = stats
54
- avgs[idx] = data
55
- start_day += 1.day
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)
56
50
  end
57
- avgs
58
51
  end
59
52
 
60
53
  # Get daily average for the past 12 months.
54
+ # @param authority_name [String] limit statistics to records for the given authority (default: all authorities)
55
+ # @param action [Symbol] one of :search, :fetch, :all_actions
56
+ # @param force [Boolean] if true, forces cache to regenerate; otherwise, returns value from cache unless expired
61
57
  # @returns [Hash] performance statistics for the past 12 months
62
58
  # @example
63
59
  # { 0: { month: '09-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
@@ -66,19 +62,12 @@ module QaServer
66
62
  # ...,
67
63
  # 11: { month: '08-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
68
64
  # }
69
- def average_last_12_months(authority_name: nil, action: nil)
70
- start_month = Time.now.getlocal.beginning_of_month - 11.months
71
- avgs = {}
72
- 0.upto(11).each do |idx|
73
- records = records_by(authority_name, action, start_month..start_month.end_of_month)
74
- stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
75
- data = {}
76
- data[BY_MONTH] = start_month.strftime("%m-%Y")
77
- data[STATS] = stats
78
- avgs[idx] = data
79
- start_month += 1.month
65
+ def average_last_12_months(authority_name: nil, action: nil, force: false)
66
+ Rails.cache.fetch("#{self.class}/#{__method__}/#{authority_name || ALL_AUTH}/#{action}/#{FOR_YEAR}",
67
+ expires_in: QaServer.cache_expiry, race_condition_ttl: 1.hour, force: force) do
68
+ Rails.logger.info("#{self.class}##{__method__} - calculating performance stats for last 12 months - cache expired or refresh requested (#{force})")
69
+ calculate_last_12_months(authority_name, action)
80
70
  end
81
- avgs
82
71
  end
83
72
 
84
73
  private
@@ -109,6 +98,51 @@ module QaServer
109
98
  ""
110
99
  end
111
100
  end
101
+
102
+ def calculate_last_24_hours(authority_name, action)
103
+ start_hour = QaServer.current_time.beginning_of_hour - 23.hours
104
+ avgs = {}
105
+ 0.upto(23).each do |idx|
106
+ records = records_by(authority_name, action, start_hour..start_hour.end_of_hour)
107
+ stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
108
+ data = {}
109
+ data[BY_HOUR] = performance_by_hour_label(idx, start_hour)
110
+ data[STATS] = stats
111
+ avgs[idx] = data
112
+ start_hour += 1.hour
113
+ end
114
+ avgs
115
+ end
116
+
117
+ def calculate_last_30_days(authority_name, action)
118
+ start_day = QaServer.current_time.beginning_of_day - 29.days
119
+ avgs = {}
120
+ 0.upto(29).each do |idx|
121
+ records = records_by(authority_name, action, start_day..start_day.end_of_day)
122
+ stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
123
+ data = {}
124
+ data[BY_DAY] = performance_by_day_label(idx, start_day)
125
+ data[STATS] = stats
126
+ avgs[idx] = data
127
+ start_day += 1.day
128
+ end
129
+ avgs
130
+ end
131
+
132
+ def calculate_last_12_months(authority_name, action)
133
+ start_month = QaServer.current_time.beginning_of_month - 11.months
134
+ avgs = {}
135
+ 0.upto(11).each do |idx|
136
+ records = records_by(authority_name, action, start_month..start_month.end_of_month)
137
+ stats = stats_calculator_class.new(records).calculate_stats(avg: true, full: false)
138
+ data = {}
139
+ data[BY_MONTH] = start_month.strftime("%m-%Y")
140
+ data[STATS] = stats
141
+ avgs[idx] = data
142
+ start_month += 1.month
143
+ end
144
+ avgs
145
+ end
112
146
  end
113
147
  end
114
148
  end
@@ -73,4 +73,9 @@ QaServer.config do |config|
73
73
  # impact and may need to be suppressed. Performance stats will not be gathered when this config is true.
74
74
  # @param [Boolean] do not gather performance data when true (defaults to false for backward compatibitily)
75
75
  # config.suppress_performance_gathering = false
76
+
77
+ # Performance data is gathered on every incoming query. Basic stats are logged from QA. Full stats are logged
78
+ # by QaServer and can eat up logging realestate. To suppress the logging of details, set this config to true.
79
+ # @param [Boolean] do not log performance data details when true (defaults to false for backward compatibitily)
80
+ # config.suppress_logging_performance_datails = false
76
81
  end
@@ -179,6 +179,15 @@ module QaServer
179
179
  @suppress_performance_gathering ||= false
180
180
  end
181
181
 
182
+ # Performance data is gathered on every incoming query. Basic stats are logged from QA. Full stats are logged
183
+ # by QaServer and can eat up logging realestate. To suppress the logging of details, set this config to true.
184
+ # @param [Boolean] do not log performance data details when true (defaults to false for backward compatibitily)
185
+ attr_writer :suppress_logging_performance_datails
186
+ def suppress_logging_performance_datails
187
+ @suppress_logging_performance_datails ||= false
188
+ end
189
+
190
+ # TODO: consider refactor performance caching to use Rails cache
182
191
  def performance_cache
183
192
  @performance_cache ||= QaServer::PerformanceCache.new
184
193
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module QaServer
3
- VERSION = '5.4.0'
3
+ VERSION = '5.5.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qa_server
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.0
4
+ version: 5.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - E. Lynette Rayle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-07 00:00:00.000000000 Z
11
+ date: 2020-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails