Cache dashboard data in the controller, not the report model

This commit is contained in:
Neil Lalonde 2013-03-20 13:54:32 -04:00
parent 1e4dd3ea0c
commit c3c25b894a
4 changed files with 92 additions and 202 deletions

View File

@ -5,6 +5,7 @@ Discourse.AdminDashboard.reopenClass({
var model = Discourse.AdminDashboard.create(); var model = Discourse.AdminDashboard.create();
return $.ajax("/admin/dashboard", { return $.ajax("/admin/dashboard", {
type: 'GET', type: 'GET',
dataType: 'json',
success: function(json) { success: function(json) {
model.mergeAttributes(json); model.mergeAttributes(json);
model.set('loaded', true); model.set('loaded', true);

View File

@ -1,6 +1,7 @@
class Admin::DashboardController < Admin::AdminController class Admin::DashboardController < Admin::AdminController
caches_action :index, expires_in: 1.hour
def index def index
render_json_dump(AdminDashboardData.fetch) render_json_dump(AdminDashboardData.fetch)
end end

View File

@ -35,98 +35,60 @@ class Report
def self.report_visits(report) def self.report_visits(report)
report.data = [] report.data = []
fetch report do UserVisit.by_day(30.days.ago).each do |date, count|
UserVisit.by_day(30.days.ago).each do |date, count| report.data << {x: date, y: count}
report.data << {x: date, y: count}
end
end end
end end
def self.report_signups(report) def self.report_signups(report)
report.data = [] report.data = []
fetch report do User.count_by_signup_date(30.days.ago).each do |date, count|
User.count_by_signup_date(30.days.ago).each do |date, count| report.data << {x: date, y: count}
report.data << {x: date, y: count}
end
end end
end end
def self.report_topics(report) def self.report_topics(report)
report.data = [] report.data = []
fetch report do Topic.count_per_day(30.days.ago).each do |date, count|
Topic.count_per_day(30.days.ago).each do |date, count| report.data << {x: date, y: count}
report.data << {x: date, y: count}
end
end end
end end
def self.report_posts(report) def self.report_posts(report)
report.data = [] report.data = []
fetch report do Post.count_per_day(30.days.ago).each do |date, count|
Post.count_per_day(30.days.ago).each do |date, count| report.data << {x: date, y: count}
report.data << {x: date, y: count}
end
end end
end end
def self.report_flags(report) def self.report_flags(report)
report.data = [] report.data = []
fetch report do (0..30).to_a.reverse.each do |i|
(0..30).to_a.reverse.each do |i| if (count = PostAction.where('date(created_at) = ?', i.days.ago.to_date).where(post_action_type_id: PostActionType.flag_types.values).count) > 0
if (count = PostAction.where('date(created_at) = ?', i.days.ago.to_date).where(post_action_type_id: PostActionType.flag_types.values).count) > 0 report.data << {x: i.days.ago.to_date.to_s, y: count}
report.data << {x: i.days.ago.to_date.to_s, y: count}
end
end end
end end
end end
def self.report_users_by_trust_level(report) def self.report_users_by_trust_level(report)
report.data = [] report.data = []
fetch report do User.counts_by_trust_level.each do |level, count|
User.counts_by_trust_level.each do |level, count| report.data << {x: level.to_i, y: count}
report.data << {x: level.to_i, y: count}
end
end end
end end
def self.report_likes(report) def self.report_likes(report)
report.data = [] report.data = []
fetch report do PostAction.count_likes_per_day(30.days.ago).each do |date, count|
PostAction.count_likes_per_day(30.days.ago).each do |date, count| report.data << {x: date, y: count}
report.data << {x: date, y: count}
end
end end
end end
def self.report_emails(report) def self.report_emails(report)
report.data = [] report.data = []
fetch report do EmailLog.count_per_day(30.days.ago).each do |date, count|
EmailLog.count_per_day(30.days.ago).each do |date, count| report.data << {x: date, y: count}
report.data << {x: date, y: count}
end
end end
end end
private
def self.fetch(report)
unless report.cache and $redis
yield
return
end
data_set = "#{report.type}:data"
if $redis.exists(data_set)
$redis.get(data_set).split('|').each do |pair|
date, count = pair.split(',')
report.data << {x: date, y: count.to_i}
end
else
yield
$redis.setex data_set, cache_expiry, report.data.map { |item| "#{item[:x]},#{item[:y]}" }.join('|')
end
rescue Redis::BaseConnectionError
yield
end
end end

View File

@ -1,162 +1,88 @@
# require 'spec_helper' require 'spec_helper'
# describe Report do describe Report do
# describe 'visits report' do
# let(:report) { Report.find('visits', cache: false) }
# describe 'visits report' do # context "no visits" do
# let(:report) { Report.find('visits', cache: false) } # it "returns an empty report" do
# report.data.should be_blank
# end
# end
# context "no visits" do # context "with visits" do
# it "returns an empty report" do # let(:user) { Fabricate(:user) }
# report.data.should be_blank
# end
# end
# context "with visits" do # before do
# let(:user) { Fabricate(:user) } # user.user_visits.create(visited_at: 1.day.ago)
# user.user_visits.create(visited_at: 2.days.ago)
# end
# before do # it "returns a report with data" do
# user.user_visits.create(visited_at: 1.day.ago) # report.data.should be_present
# user.user_visits.create(visited_at: 2.days.ago) # end
# end # end
# end
# it "returns a report with data" do # [:signup, :topic, :post, :flag, :like, :email].each do |arg|
# report.data.should be_present # describe "#{arg} report" do
# end # pluralized = arg.to_s.pluralize
# end
# end
# [:signup, :topic, :post, :flag, :like, :email].each do |arg| # let(:report) { Report.find(pluralized, cache: false) }
# describe "#{arg} report" do
# pluralized = arg.to_s.pluralize
# let(:report) { Report.find(pluralized, cache: false) } # context "no #{pluralized}" do
# it 'returns an empty report' do
# report.data.should be_blank
# end
# end
# context "no #{pluralized}" do # context "with #{pluralized}" do
# it 'returns an empty report' do # before do
# report.data.should be_blank # fabricator = case arg
# end # when :signup
# end # :user
# when :email
# :email_log
# else
# arg
# end
# Fabricate(fabricator, created_at: 25.hours.ago)
# Fabricate(fabricator, created_at: 1.hours.ago)
# Fabricate(fabricator, created_at: 1.hours.ago)
# end
# context "with #{pluralized}" do # it 'returns correct data' do
# before do # report.data[0][:y].should == 1
# fabricator = case arg # report.data[1][:y].should == 2
# when :signup # end
# :user # end
# when :email # end
# :email_log # end
# else
# arg
# end
# Fabricate(fabricator, created_at: 25.hours.ago)
# Fabricate(fabricator, created_at: 1.hours.ago)
# Fabricate(fabricator, created_at: 1.hours.ago)
# end
# it 'returns correct data' do describe 'users by trust level report' do
# report.data[0][:y].should == 1 let(:report) { Report.find('users_by_trust_level', cache: false) }
# report.data[1][:y].should == 2
# end
# end
# end
# end
# describe 'users by trust level report' do context "no users" do
# let(:report) { Report.find('users_by_trust_level', cache: false) } it "returns an empty report" do
report.data.should be_blank
end
end
# context "no users" do context "with users at different trust levels" do
# it "returns an empty report" do before do
# report.data.should be_blank 3.times { Fabricate(:user, trust_level: TrustLevel.levels[:visitor]) }
# end 2.times { Fabricate(:user, trust_level: TrustLevel.levels[:regular]) }
# end Fabricate(:user, trust_level: TrustLevel.levels[:elder])
end
# context "with users at different trust levels" do it "returns a report with data" do
# before do report.data.should be_present
# 3.times { Fabricate(:user, trust_level: TrustLevel.levels[:new]) } report.data.find {|d| d[:x] == TrustLevel.levels[:visitor]} [:y].should == 3
# 2.times { Fabricate(:user, trust_level: TrustLevel.levels[:regular]) } report.data.find {|d| d[:x] == TrustLevel.levels[:regular]}[:y].should == 2
# Fabricate(:user, trust_level: TrustLevel.levels[:moderator]) report.data.find {|d| d[:x] == TrustLevel.levels[:elder]}[:y].should == 1
# end end
end
end
# it "returns a report with data" do end
# report.data.should be_present
# report.data.find {|d| d[:x] == TrustLevel.levels[:new]} [:y].should == 3
# report.data.find {|d| d[:x] == TrustLevel.levels[:regular]}[:y].should == 2
# report.data.find {|d| d[:x] == TrustLevel.levels[:moderator]}[:y].should == 1
# end
# end
# end
# describe '#fetch' do
# context 'signups' do
# let(:report) { Report.find('signups', cache: true) }
# context 'no data' do
# context 'cache miss' do
# before do
# $redis.expects(:exists).with('signups:data').returns(false)
# end
# it 'should cache an empty data set' do
# $redis.expects(:setex).with('signups:data', Report.cache_expiry, "")
# report.data.should be_blank
# end
# end
# context 'cache hit' do
# before do
# $redis.expects(:exists).with('signups:data').returns(true)
# end
# it 'returns the cached empty report' do
# User.expects(:count_by_signup_date).never
# $redis.expects(:setex).never
# $redis.expects(:get).with('signups:data').returns('')
# report.data.should be_blank
# end
# end
# end
# context 'with data' do
# before do
# Fabricate(:user, created_at: 25.hours.ago)
# Fabricate(:user, created_at: 1.hour.ago)
# Fabricate(:user, created_at: 1.hour.ago)
# end
# context 'cache miss' do
# before do
# $redis.expects(:exists).with('signups:data').returns(false)
# end
# it 'should cache the data set' do
# $redis.expects(:setex).with do |key, expiry, string|
# string =~ /(\d)+-(\d)+-(\d)+,1/ and string =~ /(\d)+-(\d)+-(\d)+,2/
# end
# report()
# end
# it 'should return correct data' do
# report.data[0][:y].should == 1
# report.data[1][:y].should == 2
# end
# end
# context 'cache hit' do
# before do
# $redis.expects(:exists).with('signups:data').returns(true)
# end
# it 'returns the cached data' do
# User.expects(:count_by_signup_date).never
# $redis.expects(:setex).never
# $redis.expects(:get).with('signups:data').returns("#{2.days.ago.to_date.to_s},1|#{1.day.ago.to_date.to_s},2")
# report.data[0][:y].should == 1
# report.data[1][:y].should == 2
# end
# end
# end
# end
# end
# end