Dashboard calculations are done with an async job now

This commit is contained in:
Neil Lalonde 2013-08-02 18:31:25 -04:00
parent 1552c4b69e
commit 98b58150bb
11 changed files with 49 additions and 21 deletions

View File

@ -42,5 +42,9 @@ Discourse.AdminDashboardController = Ember.Controller.extend({
problemsTimestamp: function() {
return moment(this.get('problemsFetchedAt')).format('LLL');
}.property('problemsFetchedAt')
}.property('problemsFetchedAt'),
updatedTimestamp: function() {
return moment(this.get('updated_at')).format('LLL');
}.property('updated_at')
});

View File

@ -14,7 +14,7 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
},
fetchDashboardData: function(c) {
if( !c.get('dashboardFetchedAt') || moment().subtract('hour', 1).toDate() > c.get('dashboardFetchedAt') ) {
if( !c.get('dashboardFetchedAt') || moment().subtract('minutes', 30).toDate() > c.get('dashboardFetchedAt') ) {
c.set('dashboardFetchedAt', new Date());
Discourse.AdminDashboard.find().then(function(d) {
if( Discourse.SiteSettings.version_checks ){
@ -32,7 +32,7 @@ Discourse.AdminDashboardRoute = Discourse.Route.extend({
c.set('top_referrers', topReferrers);
}
['admins', 'moderators', 'blocked', 'banned', 'top_traffic_sources', 'top_referred_topics'].forEach(function(x) {
['admins', 'moderators', 'blocked', 'banned', 'top_traffic_sources', 'top_referred_topics', 'updated_at'].forEach(function(x) {
c.set(x, d[x]);
});

View File

@ -276,3 +276,9 @@
</div>
</div>
<div class='clearfix'></div>
<div class="dashboard-stats pull-right">
<div class="pull-right">{{i18n admin.dashboard.last_updated}} {{updatedTimestamp}}</div>
<div class='clearfix'></div>
</div>
<div class='clearfix'></div>

View File

@ -1,9 +1,6 @@
class Admin::DashboardController < Admin::AdminController
def index
dashboard_data = Rails.cache.fetch("admin-dashboard-data-#{Discourse::VERSION::STRING}", expires_in: 1.hour) do
AdminDashboardData.fetch_all.as_json
end
dashboard_data = AdminDashboardData.fetch_cached_stats || Jobs::DashboardStats.new.execute({})
dashboard_data.merge!({version_check: DiscourseUpdates.check_version.as_json}) if SiteSetting.version_checks?
render json: dashboard_data
end

View File

@ -41,9 +41,17 @@ class AdminDashboardData
notification_email_check ].compact
end
def self.fetch_all
def self.fetch_stats
AdminDashboardData.new
end
def self.fetch_cached_stats
# The DashboardStats job is responsible for generating and caching this.
stats = $redis.get(stats_cache_key)
stats ? JSON.parse(stats) : nil
end
def self.stats_cache_key
'dash-stats'
end
def self.fetch_problems
AdminDashboardData.new.problems
@ -58,10 +66,16 @@ class AdminDashboardData
blocked: User.blocked.count,
top_referrers: IncomingLinksReport.find('top_referrers').as_json,
top_traffic_sources: IncomingLinksReport.find('top_traffic_sources').as_json,
top_referred_topics: IncomingLinksReport.find('top_referred_topics').as_json
top_referred_topics: IncomingLinksReport.find('top_referred_topics').as_json,
updated_at: Time.zone.now.as_json
}
end
def self.recalculate_interval
# Could be configurable, but clockwork + multisite need to support it.
30 # minutes
end
def rails_env_check
I18n.t("dashboard.rails_env_warning", env: Rails.env) unless Rails.env == 'production'
end

View File

@ -239,6 +239,7 @@ class SiteSetting < ActiveRecord::Base
setting(:delete_user_max_age, 7)
setting(:delete_all_posts_max, 10)
def self.generate_api_key!
self.api_key = SecureRandom.hex(32)
end

View File

@ -35,5 +35,5 @@ module Clockwork
every(1.minute, 'clockwork_heartbeat')
every(1.minute, 'poll_mailbox')
every(30.minutes, 'destroy_old_deletion_stubs')
every(AdminDashboardData.recalculate_interval.minutes, 'dashboard_stats')
end

View File

@ -1027,6 +1027,7 @@ en:
dashboard:
title: "Dashboard"
last_updated: "Dashboard last updated:"
version: "Version"
up_to_date: "You're up to date!"
critical_available: "A critical update is available."

View File

@ -663,7 +663,6 @@ en:
delete_user_max_age: "The maximum age of a user, in days, which can be deleted by an admin."
delete_all_posts_max: "The maximum number of posts that can be deleted at once with the Delete All Posts button. If a user has more than this many posts, the posts cannot all be deleted at once and the user can't be deleted."
notification_types:
mentioned: "%{display_username} mentioned you in %{link}"
liked: "%{display_username} liked your post in %{link}"

View File

@ -0,0 +1,15 @@
module Jobs
class DashboardStats < Jobs::Base
def execute(args)
stats_json = AdminDashboardData.fetch_stats.as_json
# Add some extra time to the expiry so that the next job run has plenty of time to
# finish before previous cached value expires.
$redis.setex AdminDashboardData.stats_cache_key, (AdminDashboardData.recalculate_interval + 5).minutes, stats_json.to_json
stats_json
end
end
end

View File

@ -3,9 +3,7 @@ require_dependency 'discourse_version_check'
describe Admin::DashboardController do
before do
#NOTE: Rails.cache should be blanked between tests, at the moment we can share state with it
# that is seriously bust on quite a few levels
Rails.cache.delete("admin-dashboard-data-#{Discourse::VERSION::STRING}")
AdminDashboardData.stubs(:fetch_cached_stats).returns({reports:[]})
Jobs::VersionCheck.any_instance.stubs(:execute).returns(true)
end
@ -45,13 +43,6 @@ describe Admin::DashboardController do
json['version_check'].should_not be_present
end
end
it 'returns report data' do
xhr :get, :index
json = JSON.parse(response.body)
json.should have_key('reports')
json['reports'].should be_a(Array)
end
end
context '.problems' do