mirror of
https://github.com/codeninjasllc/discourse.git
synced 2025-02-25 07:54:11 -05:00
PERF: Cache About#stats.
This commit is contained in:
parent
8be37193ee
commit
b0ea6764e0
12 changed files with 121 additions and 19 deletions
13
app/jobs/scheduled/about_stats.rb
Normal file
13
app/jobs/scheduled/about_stats.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module Jobs
|
||||||
|
class AboutStats < Jobs::Scheduled
|
||||||
|
include Jobs::Stats
|
||||||
|
|
||||||
|
every 30.minutes
|
||||||
|
|
||||||
|
def execute(args)
|
||||||
|
stats = About.new.stats
|
||||||
|
set_cache(About, stats)
|
||||||
|
stats
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,16 +1,13 @@
|
||||||
module Jobs
|
module Jobs
|
||||||
class DashboardStats < Jobs::Scheduled
|
class DashboardStats < Jobs::Scheduled
|
||||||
|
include Jobs::Stats
|
||||||
|
|
||||||
every 30.minutes
|
every 30.minutes
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
stats = AdminDashboardData.fetch_stats.as_json
|
stats = AdminDashboardData.new.as_json
|
||||||
|
set_cache(AdminDashboardData, stats)
|
||||||
# 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.to_json
|
|
||||||
|
|
||||||
stats
|
stats
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
9
app/jobs/stats.rb
Normal file
9
app/jobs/stats.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module Jobs
|
||||||
|
module Stats
|
||||||
|
def set_cache(klass, stats)
|
||||||
|
# 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 klass.stats_cache_key, (klass.recalculate_stats_interval + 5).minutes, stats.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,18 @@
|
||||||
class About
|
class About
|
||||||
include ActiveModel::Serialization
|
include ActiveModel::Serialization
|
||||||
|
include StatsCacheable
|
||||||
|
|
||||||
attr_accessor :moderators,
|
attr_accessor :moderators,
|
||||||
:admins
|
:admins
|
||||||
|
|
||||||
|
def self.stats_cache_key
|
||||||
|
'about-stats'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.fetch_stats
|
||||||
|
About.new.stats
|
||||||
|
end
|
||||||
|
|
||||||
def version
|
def version
|
||||||
Discourse::VERSION::STRING
|
Discourse::VERSION::STRING
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require_dependency 'mem_info'
|
require_dependency 'mem_info'
|
||||||
|
|
||||||
class AdminDashboardData
|
class AdminDashboardData
|
||||||
|
include StatsCacheable
|
||||||
|
|
||||||
GLOBAL_REPORTS ||= [
|
GLOBAL_REPORTS ||= [
|
||||||
'visits',
|
'visits',
|
||||||
|
@ -57,13 +58,7 @@ class AdminDashboardData
|
||||||
|
|
||||||
|
|
||||||
def self.fetch_stats
|
def self.fetch_stats
|
||||||
AdminDashboardData.new
|
AdminDashboardData.new.as_json
|
||||||
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
|
end
|
||||||
|
|
||||||
def self.stats_cache_key
|
def self.stats_cache_key
|
||||||
|
@ -96,11 +91,6 @@ class AdminDashboardData
|
||||||
source.map { |type| Report.find(type).as_json }
|
source.map { |type| Report.find(type).as_json }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Could be configurable, multisite need to support it.
|
|
||||||
def self.recalculate_interval
|
|
||||||
30 # minutes
|
|
||||||
end
|
|
||||||
|
|
||||||
def rails_env_check
|
def rails_env_check
|
||||||
I18n.t("dashboard.rails_env_warning", env: Rails.env) unless Rails.env.production?
|
I18n.t("dashboard.rails_env_warning", env: Rails.env) unless Rails.env.production?
|
||||||
end
|
end
|
||||||
|
|
24
app/models/concerns/stats_cacheable.rb
Normal file
24
app/models/concerns/stats_cacheable.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
module StatsCacheable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def stats_cache_key
|
||||||
|
raise 'Stats cache key has not been set.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_stats
|
||||||
|
raise 'Not implemented.'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Could be configurable, multisite need to support it.
|
||||||
|
def recalculate_stats_interval
|
||||||
|
30 # minutes
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_cached_stats
|
||||||
|
# The scheduled Stats job is responsible for generating and caching this.
|
||||||
|
stats = $redis.get(stats_cache_key)
|
||||||
|
stats ? JSON.parse(stats) : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,4 +8,8 @@ class AboutSerializer < ApplicationSerializer
|
||||||
:locale,
|
:locale,
|
||||||
:version,
|
:version,
|
||||||
:https
|
:https
|
||||||
|
|
||||||
|
def stats
|
||||||
|
object.class.fetch_cached_stats || Jobs::AboutStats.new.execute({})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
10
spec/jobs/about_stats_spec.rb
Normal file
10
spec/jobs/about_stats_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Jobs::AboutStats do
|
||||||
|
it 'caches the stats' do
|
||||||
|
stats = { "visited" => 10 }
|
||||||
|
About.any_instance.expects(:stats).returns(stats)
|
||||||
|
$redis.expects(:setex).with(About.stats_cache_key, 35.minutes, stats.to_json)
|
||||||
|
expect(described_class.new.execute({})).to eq(stats)
|
||||||
|
end
|
||||||
|
end
|
10
spec/jobs/dashboard_stats_spec.rb
Normal file
10
spec/jobs/dashboard_stats_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Jobs::DashboardStats do
|
||||||
|
it 'caches the stats' do
|
||||||
|
json = { "visited" => 10 }
|
||||||
|
AdminDashboardData.any_instance.expects(:as_json).returns(json)
|
||||||
|
$redis.expects(:setex).with(AdminDashboardData.stats_cache_key, 35.minutes, json.to_json)
|
||||||
|
expect(described_class.new.execute({})).to eq(json)
|
||||||
|
end
|
||||||
|
end
|
9
spec/models/about_spec.rb
Normal file
9
spec/models/about_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe About do
|
||||||
|
|
||||||
|
describe 'stats cache' do
|
||||||
|
include_examples 'stats cachable'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -244,4 +244,8 @@ describe AdminDashboardData do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'stats cache' do
|
||||||
|
include_examples 'stats cachable'
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
23
spec/support/shared_examples_for_stats_cacheable.rb
Normal file
23
spec/support/shared_examples_for_stats_cacheable.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
shared_examples_for 'stats cachable' do
|
||||||
|
describe 'fetch_cached_stats' do
|
||||||
|
it 'returns the cached stats' do
|
||||||
|
begin
|
||||||
|
stats = { "visits" => 10 }
|
||||||
|
$redis.set(described_class.stats_cache_key, stats.to_json)
|
||||||
|
expect(described_class.fetch_cached_stats).to eq(stats)
|
||||||
|
ensure
|
||||||
|
$redis.del(described_class.stats_cache_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil if no stats has been cached' do
|
||||||
|
expect(described_class.fetch_cached_stats).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'fetch_stats' do
|
||||||
|
it 'has been implemented' do
|
||||||
|
expect{ described_class.fetch_stats }.to_not raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue