From 4e009089ac68f783e64d1076f303f2f95e53c51a Mon Sep 17 00:00:00 2001 From: Tobias Svensson Date: Mon, 16 Sep 2013 14:48:51 +0100 Subject: [PATCH] Track worker execution history. --- examples/config.ru | 1 + examples/server.rb | 2 ++ examples/workers/failing.rb | 13 +++++++ examples/workers/simple.rb | 1 - lib/sidetiq.rb | 3 ++ lib/sidetiq/config.rb | 2 +- lib/sidetiq/middleware/history.rb | 48 +++++++++++++++++++++++++ test/test_history.rb | 60 +++++++++++++++++++++++++++++++ 8 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 examples/workers/failing.rb create mode 100644 lib/sidetiq/middleware/history.rb create mode 100644 test/test_history.rb diff --git a/examples/config.ru b/examples/config.ru index baf006d..a33cc95 100644 --- a/examples/config.ru +++ b/examples/config.ru @@ -5,6 +5,7 @@ require 'sidekiq/web' require 'sidetiq/web' require './workers/simple.rb' +require './workers/failing.rb' Sidekiq.configure_client do |config| config.redis = { :size => 1 } diff --git a/examples/server.rb b/examples/server.rb index eb3efae..6536610 100644 --- a/examples/server.rb +++ b/examples/server.rb @@ -2,7 +2,9 @@ require 'sidekiq' require 'sidetiq' + require_relative 'workers/simple' +require_relative 'workers/failing' Sidekiq.logger.level = Logger::DEBUG diff --git a/examples/workers/failing.rb b/examples/workers/failing.rb new file mode 100644 index 0000000..8d3af8f --- /dev/null +++ b/examples/workers/failing.rb @@ -0,0 +1,13 @@ +class Failing + include Sidekiq::Worker + include Sidetiq::Schedulable + + recurrence { secondly } + + def perform(*args) + if rand(5) == 0 + raise "This didn't go well." + end + end +end + diff --git a/examples/workers/simple.rb b/examples/workers/simple.rb index dc34ff5..df5505c 100644 --- a/examples/workers/simple.rb +++ b/examples/workers/simple.rb @@ -5,7 +5,6 @@ class Simple recurrence { secondly } def perform(*args) - Sidekiq.logger.info "#perform" end end diff --git a/lib/sidetiq.rb b/lib/sidetiq.rb index a94f38e..82a2df6 100644 --- a/lib/sidetiq.rb +++ b/lib/sidetiq.rb @@ -20,6 +20,9 @@ require 'sidetiq/schedule' require 'sidetiq/schedulable' require 'sidetiq/version' +# middleware +require 'sidetiq/middleware/history' + # actor topology require 'sidetiq/actor/clock' require 'sidetiq/actor/handler' diff --git a/lib/sidetiq/config.rb b/lib/sidetiq/config.rb index 1eccee9..fa3625b 100644 --- a/lib/sidetiq/config.rb +++ b/lib/sidetiq/config.rb @@ -24,7 +24,7 @@ module Sidetiq end configure do |config| - config.priority = Thread.main.priority + config.worker_history = 100 config.resolution = 1 config.lock_expire = 1000 config.utc = false diff --git a/lib/sidetiq/middleware/history.rb b/lib/sidetiq/middleware/history.rb new file mode 100644 index 0000000..799e172 --- /dev/null +++ b/lib/sidetiq/middleware/history.rb @@ -0,0 +1,48 @@ +module Sidetiq + module Middleware + class History + def call(worker, msg, queue, &block) + if worker.kind_of?(Sidetiq::Schedulable) + call_with_sidetiq_history(worker, msg, queue, &block) + else + yield + end + end + + def call_with_sidetiq_history(worker, msg, queue) + entry = { + status: :success, + error: "", + exception: "", + backtrace: "", + processor: "#{Socket.gethostname}:#{Process.pid}-#{Thread.current.object_id}", + processed: Time.now.iso8601 + } + + yield + rescue StandardError => e + entry[:status] = :failure + entry[:exception] = e.class.to_s + entry[:error] = e.message + entry[:backtrace] = e.backtrace + + raise e + ensure + Sidekiq.redis do |redis| + redis.pipelined do |pipe| + list_name = "sidetiq:#{worker.class.name}:history" + + pipe.lpush(list_name, JSON.dump(entry)) + pipe.ltrim(list_name, 0, Sidetiq.config.worker_history - 1) + end + end + end + end + end +end + +Sidekiq.configure_server do |config| + config.server_middleware do |chain| + chain.add Sidetiq::Middleware::History + end +end diff --git a/test/test_history.rb b/test/test_history.rb new file mode 100644 index 0000000..77d9a10 --- /dev/null +++ b/test/test_history.rb @@ -0,0 +1,60 @@ +require_relative 'helper' + +class TestHistory < Sidetiq::TestCase + class HistoryWorker + include Sidekiq::Worker + include Sidetiq::Schedulable + end + + def test_success + middlewared do; end + + entry = Sidekiq.redis do |redis| + redis.lrange('sidetiq:TestHistory::HistoryWorker:history', 0, -1) + end + + actual = JSON.parse(entry[0], symbolize_names: true) + + assert_equal 'success', actual[:status] + + assert_empty actual[:error] + assert_empty actual[:backtrace] + assert_empty actual[:exception] + + refute_empty actual[:processor] + refute_empty actual[:processed] + end + + def test_failure + begin + middlewared do + raise StandardError.new("failed") + end + rescue + end + + entry = Sidekiq.redis do |redis| + redis.lrange('sidetiq:TestHistory::HistoryWorker:history', 0, -1) + end + + actual = JSON.parse(entry[0], symbolize_names: true) + + assert_equal 'failure', actual[:status] + + assert_equal "failed", actual[:error] + assert_equal "StandardError", actual[:exception] + refute_empty actual[:backtrace] + + refute_empty actual[:processor] + refute_empty actual[:processed] + end + + def middlewared + middleware = Sidetiq::Middleware::History.new + + middleware.call(HistoryWorker.new, {}, 'default') do + yield + end + end +end +