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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +53 -0
  4. data/app/controllers/{qa_server/authority_validation_controller.rb → concerns/qa_server/authority_validation_behavior.rb} +13 -11
  5. data/app/controllers/qa_server/authority_list_controller.rb +5 -1
  6. data/app/controllers/qa_server/check_status_controller.rb +5 -1
  7. data/app/controllers/qa_server/monitor_status_controller.rb +40 -28
  8. data/app/jobs/qa_server/monitor_tests_job.rb +50 -0
  9. data/app/models/qa_server/performance_cache.rb +11 -3
  10. data/app/models/qa_server/performance_history.rb +24 -106
  11. data/app/models/qa_server/scenario_run_history.rb +161 -176
  12. data/app/models/qa_server/scenario_run_registry.rb +4 -4
  13. data/app/prepends/prepended_linked_data/find_term.rb +4 -4
  14. data/app/prepends/prepended_linked_data/search_query.rb +4 -4
  15. data/app/prepends/prepended_rdf/rdf_graph.rb +4 -4
  16. data/app/presenters/concerns/qa_server/monitor_status/performance_datatable_behavior.rb +23 -1
  17. data/app/presenters/qa_server/check_status_presenter.rb +4 -4
  18. data/app/presenters/qa_server/monitor_status/current_status_presenter.rb +17 -5
  19. data/app/presenters/qa_server/monitor_status/history_presenter.rb +40 -19
  20. data/app/presenters/qa_server/monitor_status/performance_presenter.rb +3 -1
  21. data/app/presenters/qa_server/monitor_status_presenter.rb +9 -9
  22. data/app/services/qa_server/monitor_cache_service.rb +22 -0
  23. data/app/services/qa_server/performance_calculator_service.rb +18 -7
  24. data/app/services/qa_server/performance_datatable_service.rb +82 -0
  25. data/app/services/qa_server/performance_graph_data_service.rb +140 -82
  26. data/app/services/qa_server/performance_graphing_service.rb +15 -12
  27. data/app/services/qa_server/time_period_service.rb +93 -0
  28. data/app/services/qa_server/time_service.rb +29 -0
  29. data/app/validators/qa_server/scenario_validator.rb +3 -3
  30. data/app/validators/qa_server/search_scenario_validator.rb +3 -3
  31. data/app/views/qa_server/monitor_status/_performance.html.erb +2 -1
  32. data/app/views/qa_server/monitor_status/_test_history.html.erb +1 -2
  33. data/app/views/qa_server/monitor_status/_test_summary.html.erb +2 -2
  34. data/config/locales/qa_server.en.yml +3 -4
  35. data/lib/generators/qa_server/templates/config/initializers/qa_server.rb +4 -0
  36. data/lib/qa_server.rb +0 -23
  37. data/lib/qa_server/configuration.rb +20 -0
  38. data/lib/qa_server/version.rb +1 -1
  39. data/spec/lib/qa_server_spec.rb +0 -51
  40. data/spec/services/qa_server/monitor_cache_service_spec.rb +20 -0
  41. data/spec/services/qa_server/time_period_service_spec.rb +246 -0
  42. data/spec/services/qa_server/time_service_spec.rb +50 -0
  43. metadata +14 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 151dd2fd2491a1d6ea72b18356db77ec566bd513
4
- data.tar.gz: a3f1e776af8ba78e91c754571cbed0c97ed3306e
3
+ metadata.gz: 860edda3341250dd5f1fc4db164b95f8f24bf832
4
+ data.tar.gz: 89cb1ccc0f86ef647d490dd416bf27b00f598d86
5
5
  SHA512:
6
- metadata.gz: da0fe6ca4b13e58d7c263b585a391c9988786e06b16db3d3ed016c25db128f0e260a8fef17f774881f2037fe310a7eea1eb93c75a391ca5249b4a7437cc7a28a
7
- data.tar.gz: c7d4b2bbc9532cce06f4ec96451780c0f5fffc04557cf82004796ac5247bb213390d7ca70f7fb5c35327075ae6c97f83557fa18652c323ff158d2dddd7c7ab84
6
+ metadata.gz: f43f63cbfda11c203e79fb8aeeb4a8f05dac3051ea4225593fe82244343d35b8076a3db11672c913bf80eb8df8cfd0216bc7a6218d296c9772b516e32edeb41f
7
+ data.tar.gz: 9192ba67322874947fc9f979d7545369de86d041a533361621ac091013cbb04230b0f756d2b6d570a0c5fc992dce27548d6b86dfd93ff95733c39a134b542546
@@ -1,3 +1,15 @@
1
+ ### 6.0.0 (2020-02-13)
2
+
3
+ * refactor generation of performance graphs to minimize db access and calculations
4
+ * shorten race_condition times for caching
5
+ * rename jobs_logger to be monitor_logger
6
+ * run monitoring tests in background
7
+ * move methods from QaServer to services
8
+ * use presenter to get failure data
9
+ * move controller validation code to module include
10
+ * limit historical data to configurable time period
11
+ * move time_period where clause construction to service
12
+
1
13
  ### 5.5.1 (2020-01-29)
2
14
 
3
15
  * fix - check for nil before calling .each
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # QaServer
2
+
2
3
  This rails engine can be installed into your app to serve as a Questioning Authority (QA) Server for accessing external authorities. It is part of a larger architecture supporting linked data authority access. From this engine, you can send a search query and get back multiple results OR you can fetch a single term. The engine provides UI for monitoring connections to configured authorities and the ability to check the current status of a single authority to determine if it is up and running now.
3
4
 
4
5
  ## Reference
@@ -25,6 +26,7 @@ Only required if you want to generate status charts. Generated charts will be s
25
26
  `app/assets/images/qa_server/charts` directory. By default status is displayed in a table.
26
27
 
27
28
  1. [ImageMagick](http://www.imagemagick.org/)
29
+ 2. job queue processor of your choice
28
30
 
29
31
  ### Installation Instructions
30
32
 
@@ -58,6 +60,57 @@ $ rake db:migrate
58
60
 
59
61
  If upgrading instead of installing, see the Release notes for steps you may need to take manually since you won't be running the installer.
60
62
 
63
+ #### Setting up monitoring
64
+
65
+ Monitoring runs once a day as a background job.
66
+
67
+ ##### Configuring cache expiration
68
+
69
+ There are several configurations that control when the monitoring job will execute.
70
+
71
+ ```
72
+ # Preferred time zone for reporting historical data and performance data
73
+ # @param [String] time zone name
74
+ # @see https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html for possible values of TimeZone names
75
+ attr_writer :preferred_time_zone_name
76
+ def preferred_time_zone_name
77
+ @preferred_time_zone_name ||= 'Eastern Time (US & Canada)'
78
+ end
79
+
80
+ # Set preferred hour to expire caches related to slow running calculations (e.g. monitoring tests, performance data)
81
+ # @param [Integer] count of hours after midnight (0-23 with 0=midnight)
82
+ # @raise [ArgumentError] if offset is not between 0 and 23
83
+ # @example
84
+ # For preferred_time_zone_name of 'Eastern Time (US & Canada)', use 3 for slow down at midnight PT/3am ET
85
+ # For preferred_time_zone_name of 'Pacific Time (US & Canada)', use 0 for slow down at midnight PT/3am ET
86
+ def hour_offset_to_expire_cache=(offset)
87
+ raise ArgumentError, 'offset must be between 0 and 23' unless (0..23).cover? offset
88
+ @hour_offset_to_expire_cache = offset
89
+ end
90
+ ```
91
+
92
+ With the default values set for the cache configurations, the cache will expire at midnight Pacific Time/3am Eastern Time.
93
+
94
+ ##### Process for updating monitoring tests
95
+
96
+ Monitoring works in conjuction with the cache. When the cache expires, the monitoring tests will run the next time the monitor status page is accessed. When the monitor status page loads, it attempts to get the data for the page from the cache. If the cache has expired, it will get the previous day's data and kick off a background job to run the tests and update the cache.
97
+
98
+ ##### Controlling when tests run
99
+
100
+ Because this process is launched by access to the monitor status page, it will not by default run the monitoring tests everyday. In our system, we set up Pingdom to access the monitor status page hourly around the clock. If an error occurs during the latest testing, the monitor status page includes a CSS class `summary-status-bad` and displays error results. Pingdom is configured to send a notification if the `summary-status-bad` CSS class is on the page, letting us know that we need to look into a problem with authority access.
101
+
102
+ ##### Setting up background jobs
103
+
104
+ Reference: https://guides.rubyonrails.org/active_job_basics.html
105
+
106
+ Since this job is not critical for end users, you can set up jobs to be processed by the ruby provided in-memory job queue. The risk here is that if the server restarts or some other failure occurs, jobs in that queue are lost. If this is unacceptable for your system, then you will want to setup a 3rd party job queue ([more info...](https://guides.rubyonrails.org/active_job_basics.html#starting-the-backend)).
107
+
108
+ To configure in-memory job queue, add the following to config/environments/production.rb
109
+
110
+ ```
111
+ config.active_job.queue_adapter = :async # runs in-memory; a crash will lose the job
112
+ ```
113
+
61
114
  #### Test the install
62
115
 
63
116
  * Start rails server with `rails s`
@@ -1,21 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
  module QaServer
3
- class AuthorityValidationController < ApplicationController
4
- layout 'qa_server'
5
-
6
- class_attribute :validator_class,
7
- :lister_class,
8
- :logger_class
9
-
10
- self.validator_class = QaServer::AuthorityValidatorService
11
- self.lister_class = QaServer::AuthorityListerService
12
- self.logger_class = QaServer::ScenarioLogger
3
+ module AuthorityValidationBehavior
4
+ extend ActiveSupport::Concern
13
5
 
14
6
  VALIDATION_TYPE_PARAM = :validation_type
15
7
  VALIDATE_CONNECTIONS = 'connections'
16
8
  VALIDATE_ACCURACY = 'accuracy'
17
9
  ALL_VALIDATIONS = 'all_checks'
18
- DEFAULT_VALIDATION_TYPE = validator_class::VALIDATE_CONNECTIONS
10
+ DEFAULT_VALIDATION_TYPE = QaServer::AuthorityValidatorService
11
+
12
+ included do
13
+ class_attribute :validator_class,
14
+ :lister_class,
15
+ :logger_class
16
+
17
+ self.validator_class = QaServer::AuthorityValidatorService
18
+ self.lister_class = QaServer::AuthorityListerService
19
+ self.logger_class = QaServer::ScenarioLogger
20
+ end
19
21
 
20
22
  private
21
23
 
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
  # Controller for Authorities header menu item
3
3
  module QaServer
4
- class AuthorityListController < QaServer::AuthorityValidationController
4
+ class AuthorityListController < ApplicationController
5
+ layout 'qa_server'
6
+
7
+ include QaServer::AuthorityValidationBehavior
8
+
5
9
  class_attribute :presenter_class
6
10
  self.presenter_class = QaServer::AuthorityListPresenter
7
11
 
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
  # Controller for Check Status header menu item
3
3
  module QaServer
4
- class CheckStatusController < QaServer::AuthorityValidationController
4
+ class CheckStatusController < ApplicationController
5
+ layout 'qa_server'
6
+
7
+ include QaServer::AuthorityValidationBehavior
8
+
5
9
  ALL_AUTHORITIES = '__all__'
6
10
 
7
11
  class_attribute :presenter_class
@@ -1,45 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
  # Controller for Monitor Status header menu item
3
3
  module QaServer
4
- class MonitorStatusController < QaServer::AuthorityValidationController
4
+ class MonitorStatusController < ApplicationController
5
+ layout 'qa_server'
6
+
7
+ include QaServer::AuthorityValidationBehavior
8
+
5
9
  class_attribute :presenter_class,
6
10
  :scenario_run_registry_class,
7
11
  :scenario_history_class,
8
- :performance_history_class
12
+ :performance_history_class,
13
+ :graphing_service_class
9
14
  self.presenter_class = QaServer::MonitorStatusPresenter
10
15
  self.scenario_run_registry_class = QaServer::ScenarioRunRegistry
11
16
  self.scenario_history_class = QaServer::ScenarioRunHistory
12
17
  self.performance_history_class = QaServer::PerformanceHistory
18
+ self.graphing_service_class = QaServer::PerformanceGraphingService
13
19
 
14
20
  # Sets up presenter with data to display in the UI
15
21
  def index
16
- latest_run
22
+ log_header
23
+ latest_test_run
17
24
  @presenter = presenter_class.new(current_summary: latest_summary,
18
25
  current_failure_data: latest_failures,
19
26
  historical_summary_data: historical_data,
20
- performance_data: performance_data)
27
+ performance_data: performance_table_data)
28
+ update_performance_graphs
21
29
  render 'index', status: :internal_server_error if latest_summary.failing_authority_count.positive?
22
30
  end
23
31
 
24
32
  private
25
33
 
26
- # Sets @latest_run [QaServer::ScenarioRunRegistry]
27
- def 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)
34
+ # Sets @latest_test_run [QaServer::ScenarioRunRegistry]
35
+ def latest_test_run
36
+ @latest_test_run ||= latest_test_run_from_cache
37
+ end
38
+
39
+ # cache of latest run; runs tests if cache is expired
40
+ # @see #latest_test_run_from_temp_cache
41
+ def latest_test_run_from_cache
42
+ Rails.cache.fetch("#{self.class}/#{__method__}", expires_in: QaServer::MonitorCacheService.cache_expiry, race_condition_ttl: 5.minutes, force: refresh_tests?) do
43
+ QaServer.config.monitor_logger.info("(#{self.class}##{__method__}) get latest run of monitoring tests - cache expired or refresh requested (force: #{refresh_tests?})")
44
+ QaServer::MonitorTestsJob.perform_later
32
45
  scenario_run_registry_class.latest_run
33
46
  end
34
47
  end
35
48
 
36
49
  # Sets @latest_summary [QaServer::ScenarioRunSummary]
37
50
  def latest_summary
38
- scenario_history_class.run_summary(scenario_run: latest_run, force: refresh_tests?)
51
+ scenario_history_class.run_summary(scenario_run: latest_test_run, force: refresh_tests?)
39
52
  end
40
53
 
41
54
  def latest_failures
42
- scenario_history_class.run_failures(run_id: latest_run.id, force: refresh_tests?)
55
+ scenario_history_class.run_failures(run_id: latest_test_run.id, force: refresh_tests?)
43
56
  end
44
57
 
45
58
  # Sets @historical_data [Array<Hash>]
@@ -47,16 +60,15 @@ module QaServer
47
60
  scenario_history_class.historical_summary(force: refresh_history?)
48
61
  end
49
62
 
50
- # Sets @performance_data [Hash<Hash>]
51
- def performance_data
52
- performance_history_class.performance_data(datatype: performance_datatype, force: refresh_performance?)
63
+ # Sets @performance_table_data [Hash<Hash>]
64
+ def performance_table_data
65
+ display_performance_datatable? ? performance_history_class.performance_table_data(force: refresh_performance?) : {}
53
66
  end
54
67
 
55
- def performance_datatype
56
- return :all if display_performance_datatable? && display_performance_graph?
57
- return :datatable if display_performance_datatable?
58
- return :graph if display_performance_graph?
59
- :none
68
+ def update_performance_graphs
69
+ return unless display_performance_graph?
70
+ data = performance_history_class.performance_graph_data(force: refresh_performance?)
71
+ graphing_service_class.create_performance_graphs(performance_data: data)
60
72
  end
61
73
 
62
74
  def display_performance_datatable?
@@ -67,14 +79,6 @@ module QaServer
67
79
  @display_performance_graph ||= QaServer.config.display_performance_graph?
68
80
  end
69
81
 
70
- def refresh_history
71
- historical_summary_data(refresh: refresh_history?)
72
- end
73
-
74
- def refresh_performance
75
- performance_data(refresh: refresh_performance?)
76
- end
77
-
78
82
  def refresh?
79
83
  params.key? :refresh
80
84
  end
@@ -98,5 +102,13 @@ module QaServer
98
102
  return false unless refresh?
99
103
  refresh_all? || params[:refresh].casecmp?('performance')
100
104
  end
105
+
106
+ def log_header
107
+ QaServer.config.monitor_logger.debug("----------------------------------------------------------------------")
108
+ QaServer.config.monitor_logger.debug(" loading monitor status page")
109
+ QaServer.config.monitor_logger.debug("----------------------------------------------------------------------")
110
+ QaServer.config.monitor_logger.info("(#{self.class}##{__method__}) monitor status page request (refresh_tests? # #{refresh_tests?}, " \
111
+ "refresh_history? # #{refresh_history?}, refresh_performance? # #{refresh_performance?})")
112
+ end
101
113
  end
102
114
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ # Job to run monitoring tests
3
+ module QaServer
4
+ class MonitorTestsJob < ApplicationJob
5
+ include QaServer::AuthorityValidationBehavior
6
+
7
+ queue_as :default
8
+
9
+ class_attribute :scenario_run_registry_class
10
+ self.scenario_run_registry_class = QaServer::ScenarioRunRegistry
11
+
12
+ # def perform(job_id:)
13
+ def perform
14
+ Rails.cache.fetch("QaServer::MonitorTestsController/latest_run", expires_in: QaServer::MonitorCacheService.cache_expiry, race_condition_ttl: 5.minutes, force: true) do
15
+ job_id = SecureRandom.uuid
16
+ monitor_tests_job_id = job_id unless monitor_tests_job_id
17
+ if monitor_tests_job_id == job_id # avoid race conditions
18
+ QaServer.config.monitor_logger.info("(#{self.class}##{__method__}-#{job_id}) RUNNING monitoring tests")
19
+ validate(authorities_list)
20
+ scenario_run_registry_class.save_run(scenarios_results: status_log.to_a)
21
+ QaServer.config.monitor_logger.info("(#{self.class}##{__method__}-#{job_id}) COMPLETED monitoring tests")
22
+ reset_monitor_tests_job_id
23
+ end
24
+ scenario_run_registry_class.latest_run
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # @return [String, Boolean] Returns job id of the job currently running tests; otherwise, false if tests are not running
31
+ def monitor_tests_job_id
32
+ Rails.cache.fetch("QaServer:monitor_tests-job_id", expires_in: 2.hours, race_condition_ttl: 5.minutes) { false }
33
+ end
34
+
35
+ # Set the id of the job that will run the tests.
36
+ # @param job_id [String] UUID for job running the tests
37
+ def monitor_tests_job_id=(job_id)
38
+ # check to see if there is a current job already running tests
39
+ current_job_id = Rails.cache.fetch("QaServer:monitor_tests-job_id", expires_in: 2.hours, race_condition_ttl: 30.seconds) { job_id }
40
+
41
+ # current_job_id may be false meaning tests are not currently running; in which case, it is ok to force set job_id
42
+ Rails.cache.fetch("QaServer:monitor_tests-job_id", expires_in: 2.hours, race_condition_ttl: 30.seconds, force: true) { job_id } unless current_job_id
43
+ end
44
+
45
+ # Set job id for monitor tests to false indicating that tests are not currently running
46
+ def reset_monitor_tests_job_id
47
+ Rails.cache.fetch("QaServer:monitor_tests-job_id", expires_in: 2.hours, race_condition_ttl: 30.seconds, force: true) { false }
48
+ end
49
+ end
50
+ end
@@ -7,7 +7,7 @@ module QaServer
7
7
  end
8
8
 
9
9
  def new_entry(authority:, action:)
10
- entry = { dt_stamp: QaServer.current_time,
10
+ entry = { dt_stamp: QaServer::TimeService.current_time,
11
11
  authority: authority,
12
12
  action: action }
13
13
  id = SecureRandom.uuid
@@ -36,8 +36,7 @@ module QaServer
36
36
  normalization_time_ms: entry[:normalization_time_ms])
37
37
  @cache.delete(id)
38
38
  end
39
- Rails.logger.warn("0 of #{size_before} performance data records were saved") if size_before.positive? && (size_before == @cache.size)
40
- Rails.logger.info("#{size_before - @cache.size} of #{size_before} performance data records were saved") if size_before.positive? && (size_before > @cache.size)
39
+ log_write_all("(#{self.class}##{__method__})", size_before, @cache.size)
41
40
  end
42
41
 
43
42
  def log(id:)
@@ -63,5 +62,14 @@ module QaServer
63
62
  :graph_load_time_ms,
64
63
  :normalization_time_ms]
65
64
  end
65
+
66
+ def log_write_all(prefix, size_before, cache_size)
67
+ if size_before.positive?
68
+ QaServer.config.monitor_logger.warn("#{prefix} 0 of #{size_before} performance data records were saved") if size_before == cache_size
69
+ QaServer.config.monitor_logger.info("#{prefix} #{size_before - cache_size} of #{size_before} performance data records were saved") if size_before > cache_size
70
+ else
71
+ QaServer.config.monitor_logger.info("#{prefix} 0 of 0 performance data records were saved")
72
+ end
73
+ end
66
74
  end
67
75
  end
@@ -6,11 +6,9 @@ module QaServer
6
6
 
7
7
  enum action: [:fetch, :search]
8
8
 
9
- class_attribute :stats_calculator_class, :graph_data_service_class, :graphing_service_class, :authority_list_class
10
- self.stats_calculator_class = QaServer::PerformanceCalculatorService
9
+ class_attribute :datatable_data_service_class, :graph_data_service_class
10
+ self.datatable_data_service_class = QaServer::PerformanceDatatableService
11
11
  self.graph_data_service_class = QaServer::PerformanceGraphDataService
12
- self.graphing_service_class = QaServer::PerformanceGraphingService
13
- self.authority_list_class = QaServer::AuthorityListerService
14
12
 
15
13
  class << self
16
14
  include QaServer::PerformanceHistoryDataKeys
@@ -20,12 +18,30 @@ module QaServer
20
18
  # @param action [Symbol] type of action being evaluated (e.g. :fetch, :search)
21
19
  # @param dt_stamp [Time] defaults to current time in preferred time zone
22
20
  # @return ActveRecord::Base for the new performance history record
23
- def create_record(authority:, action:, dt_stamp: QaServer.current_time)
21
+ def create_record(authority:, action:, dt_stamp: QaServer::TimeService.current_time)
24
22
  create(dt_stamp: dt_stamp,
25
23
  authority: authority,
26
24
  action: action)
27
25
  end
28
26
 
27
+ # Performance data for a day, a month, a year, and all time for each authority.
28
+ # @param datatype [Symbol] what type of data should be calculated (e.g. :datatable, :graph, :all)
29
+ # @returns [Hash] performance statistics for the past 24 hours
30
+ # @example
31
+ # { all_authorities:
32
+ # { search:
33
+ # { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
34
+ # retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
35
+ # retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 },
36
+ # fetch: { ... # same data as for search_stats },
37
+ # all: { ... # same data as for search_stats }
38
+ # },
39
+ # AGROVOC_LD4L_CACHE: { ... # same data for each authority }
40
+ # }
41
+ def performance_table_data(force: false)
42
+ datatable_data_service_class.calculate_datatable_data(force: force)
43
+ end
44
+
29
45
  # Performance data for a day, a month, a year, and all time for each authority.
30
46
  # @param datatype [Symbol] what type of data should be calculated (e.g. :datatable, :graph, :all)
31
47
  # @returns [Hash] performance statistics for the past 24 hours
@@ -33,10 +49,6 @@ module QaServer
33
49
  # { all_authorities:
34
50
  # { search:
35
51
  # {
36
- # datatable_stats:
37
- # { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
38
- # retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
39
- # retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 }
40
52
  # day:
41
53
  # { 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. }},
42
54
  # 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. }},
@@ -59,108 +71,14 @@ module QaServer
59
71
  # 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. }}
60
72
  # }
61
73
  # },
62
- # fetch: { ... # same data as for search_stats }
74
+ # fetch: { ... # same data as for search_stats },
63
75
  # all: { ... # same data as for search_stats }
64
76
  # },
65
77
  # AGROVOC_LD4L_CACHE: { ... # same data for each authority }
66
78
  # }
67
- def performance_data(datatype: :datatable, force: false)
68
- return if datatype == :none
69
- QaServer.config.performance_cache.write_all
70
- data = calculate_data(datatype, force: force)
71
- graphing_service_class.create_performance_graphs(performance_data: data) if calculate_graphdata? datatype
72
- data
79
+ def performance_graph_data(force: false)
80
+ graph_data_service_class.calculate_graph_data(force: force)
73
81
  end
74
-
75
- private
76
-
77
- def calculate_datatable?(datatype)
78
- datatype == :datatable || datatype == :all
79
- end
80
-
81
- def calculate_graphdata?(datatype)
82
- datatype == :graph || datatype == :all
83
- end
84
-
85
- def calculate_data(datatype, force:)
86
- data = {}
87
- auths = authority_list_class.authorities_list
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
- data
91
- end
92
-
93
- def data_for_authority(authority_name: nil, datatype:, force:)
94
- action_data = {}
95
- [:search, :fetch, :all_actions].each do |action|
96
- data = {}
97
- data[FOR_DATATABLE] = data_table_stats(authority_name, action, force: force) if calculate_datatable?(datatype)
98
- if calculate_graphdata?(datatype)
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
- end
103
- action_data[action] = data
104
- end
105
- action_data
106
- end
107
-
108
- # Get statistics for all available data.
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
112
- # @returns [Hash] performance statistics for the datatable during the expected time period
113
- # @example
114
- # { retrieve_avg_ms: 12.3, graph_load_avg_ms: 2.1, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5,
115
- # retrieve_10th_ms: 12.3, graph_load_10th_ms: 12.3, normalization_10th_ms: 4.2, full_request_10th_ms: 16.5,
116
- # retrieve_90th_ms: 12.3, graph_load_90th_ms: 12.3, normalization_90th_ms: 4.2, full_request_90th_ms: 16.5 }
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
126
- end
127
-
128
- def expected_time_period
129
- QaServer.config.performance_datatable_default_time_period
130
- end
131
-
132
- def records_for_last_24_hours(auth_name)
133
- return unless expected_time_period == :day
134
- end_hour = QaServer.current_time
135
- start_hour = end_hour - 23.hours
136
- where_clause = { dt_stamp: start_hour..end_hour }
137
- records_for_authority(auth_name, where_clause)
138
- end
139
-
140
- def records_for_last_30_days(auth_name)
141
- return unless expected_time_period == :month
142
- end_day = QaServer.current_time
143
- start_day = end_day - 29.days
144
- where_clause = { dt_stamp: start_day..end_day }
145
- records_for_authority(auth_name, where_clause)
146
- end
147
-
148
- def records_for_last_12_months(auth_name)
149
- return unless expected_time_period == :year
150
- end_month = QaServer.current_time
151
- start_month = end_month - 11.months
152
- where_clause = { dt_stamp: start_month..end_month }
153
- records_for_authority(auth_name, where_clause)
154
- end
155
-
156
- def all_records(auth_name)
157
- auth_name.nil? ? PerformanceHistory.all : where(authority: auth_name)
158
- end
159
-
160
- def records_for_authority(auth_name, where_clause)
161
- where_clause[:authority] = auth_name unless auth_name.nil?
162
- where(where_clause)
163
- end
164
82
  end
165
83
  end
166
84
  end