From 8cc7f3c20b6e2bd0fc8ccf9ebe164658a4b08a89 Mon Sep 17 00:00:00 2001
From: Neil Lalonde <neillalonde@gmail.com>
Date: Thu, 21 Mar 2013 16:51:19 -0400
Subject: [PATCH] Dashboard warning when clockwork doesn't seem to be running

---
 app/models/admin_dashboard_data.rb            |  6 +++-
 config/clock.rb                               |  1 +
 config/locales/server.en.yml                  |  1 +
 lib/jobs/clockwork_heartbeat.rb               | 31 +++++++++++++++++++
 .../jobs/clockwork_heartbeat_spec.rb          | 26 ++++++++++++++++
 spec/models/admin_dashboard_data_spec.rb      | 14 +++++++++
 6 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 lib/jobs/clockwork_heartbeat.rb
 create mode 100644 spec/components/jobs/clockwork_heartbeat_spec.rb

diff --git a/app/models/admin_dashboard_data.rb b/app/models/admin_dashboard_data.rb
index 07a7d4130..596443558 100644
--- a/app/models/admin_dashboard_data.rb
+++ b/app/models/admin_dashboard_data.rb
@@ -9,7 +9,7 @@ class AdminDashboardData
   def as_json
     @json ||= {
       reports: REPORTS.map { |type| Report.find(type) },
-      problems: [rails_env_check, host_names_check, gc_checks].compact
+      problems: [rails_env_check, host_names_check, gc_checks, clockwork_check].compact
     }.merge(
       SiteSetting.version_checks? ? {version_check: DiscourseUpdates.check_version} : {}
     )
@@ -26,4 +26,8 @@ class AdminDashboardData
   def gc_checks
     I18n.t("dashboard.gc_warning") if ENV['RUBY_GC_MALLOC_LIMIT'].nil?
   end
+
+  def clockwork_check
+    I18n.t('dashboard.clockwork_warning') unless Jobs::ClockworkHeartbeat.is_clockwork_running?
+  end
 end
\ No newline at end of file
diff --git a/config/clock.rb b/config/clock.rb
index edb6078ec..ae1a5aaf9 100644
--- a/config/clock.rb
+++ b/config/clock.rb
@@ -17,4 +17,5 @@ module Clockwork
   every(1.minute, 'calculate_score')
   every(20.minutes, 'calculate_view_counts')
   every(1.day, 'version_check')
+  every(1.minute, 'clockwork_heartbeat')
 end
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 00b87618e..86d409c3e 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -284,6 +284,7 @@ en:
     rails_env_warning: "Your server is running in %{env} mode."
     host_names_warning: "Your config/database.yml file is using the default localhost hostname. Update it to use your site's hostname."
     gc_warning: 'Your server is using default ruby garbage collection parameters, which will not give you the best performance. Read this topic on performance tuning: <a href="http://meta.discourse.org/t/tuning-ruby-and-rails-for-discourse/4126">Tuning Ruby and Rails for Discourse</a>.'
+    clockwork_warning: 'Clockwork is not running. Ensure that a clockwork process is always running so that important jobs can be scheduled. <a href="https://github.com/tomykaira/clockwork">Learn about clockwork here</a>.'
 
   site_settings:
     default_locale: "The default language of this Discourse instance (ISO 639-1 Code)"
diff --git a/lib/jobs/clockwork_heartbeat.rb b/lib/jobs/clockwork_heartbeat.rb
new file mode 100644
index 000000000..10134ffbe
--- /dev/null
+++ b/lib/jobs/clockwork_heartbeat.rb
@@ -0,0 +1,31 @@
+module Jobs
+  class ClockworkHeartbeat < Jobs::Base
+
+    def execute(args)
+      $redis.set last_heartbeat_at_key, Time.now.to_i
+    end
+
+    def self.is_clockwork_running?
+      if time = ClockworkHeartbeat.new.last_heartbeat_at
+        time > 2.minutes.ago
+      else
+        false
+      end
+    end
+
+    def last_heartbeat_at
+      if time_int = $redis.get(last_heartbeat_at_key)
+        Time.at(time_int.to_i)
+      else
+        nil
+      end
+    end
+
+    private
+
+      def last_heartbeat_at_key
+        'clockwork:last_heartbeat_at'
+      end
+
+  end
+end
\ No newline at end of file
diff --git a/spec/components/jobs/clockwork_heartbeat_spec.rb b/spec/components/jobs/clockwork_heartbeat_spec.rb
new file mode 100644
index 000000000..46240d327
--- /dev/null
+++ b/spec/components/jobs/clockwork_heartbeat_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+require 'jobs'
+
+describe Jobs::ClockworkHeartbeat do
+
+  describe '#is_clockwork_running?' do
+
+    subject { Jobs::ClockworkHeartbeat.is_clockwork_running? }
+
+    it 'returns false if last_heartbeat_at is nil' do
+      Jobs::ClockworkHeartbeat.any_instance.stubs(:last_heartbeat_at).returns(nil)
+      subject.should be_false
+    end
+
+    it 'returns false if last_heartbeat_at is more than 2 minutes ago' do
+      Jobs::ClockworkHeartbeat.any_instance.stubs(:last_heartbeat_at).returns(10.minutes.ago)
+      subject.should be_false
+    end
+
+    it 'returns true if last_heartbeat_at is more recent than 2 minutes ago' do
+      Jobs::ClockworkHeartbeat.any_instance.stubs(:last_heartbeat_at).returns(Time.zone.now)
+      subject.should be_true
+    end
+  end
+
+end
\ No newline at end of file
diff --git a/spec/models/admin_dashboard_data_spec.rb b/spec/models/admin_dashboard_data_spec.rb
index 9020b955d..e285879ea 100644
--- a/spec/models/admin_dashboard_data_spec.rb
+++ b/spec/models/admin_dashboard_data_spec.rb
@@ -54,4 +54,18 @@ describe AdminDashboardData do
     end
   end
 
+  describe 'clockwork_check' do
+    subject { AdminDashboardData.new.clockwork_check }
+
+    it 'returns nil when clockwork is running' do
+      Jobs::ClockworkHeartbeat.stubs(:is_clockwork_running?).returns(true)
+      subject.should be_nil
+    end
+
+    it 'returns a string when clockwork is not running' do
+      Jobs::ClockworkHeartbeat.stubs(:is_clockwork_running?).returns(false)
+      subject.should_not be_nil
+    end
+  end
+
 end
\ No newline at end of file