From 0a0536bd84b3cd60bac23e82423f55f6b8047506 Mon Sep 17 00:00:00 2001 From: Daan Davidsz <daan@roqua.nl> Date: Wed, 15 Aug 2018 07:14:34 +0000 Subject: [PATCH] Delayed Job monitoring --- Gemfile | 1 + Gemfile.lock | 4 +- README.md | 8 ++ .../delayed_job/activity_monitoring.rb | 32 ++++++++ .../delayed_job/activity_monitoring_spec.rb | 75 +++++++++++++++++++ 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 lib/roqua/core_ext/delayed_job/activity_monitoring.rb create mode 100644 spec/roqua/core_ext/delayed_job/activity_monitoring_spec.rb diff --git a/Gemfile b/Gemfile index 280e35d..8544661 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ group :test do gem 'active_interaction', '~> 3.0' gem 'appsignal' gem 'combustion', '~> 0.5.2' + gem 'fakefs', require: 'fakefs/safe' gem 'guard-rspec', '~> 4.2.6' gem 'responders' gem 'rspec-instrumentation-matcher' diff --git a/Gemfile.lock b/Gemfile.lock index 6532b6c..25e647b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,6 +65,7 @@ GEM delayed_job (>= 3.0, < 5) diff-lcs (1.3) erubis (2.7.0) + fakefs (0.15.0) ffi (1.9.17) formatador (0.2.5) guard (2.14.0) @@ -183,6 +184,7 @@ DEPENDENCIES bundler (~> 1.0) combustion (~> 0.5.2) delayed_job_active_record + fakefs guard-rspec (~> 4.2.6) rake responders @@ -195,4 +197,4 @@ DEPENDENCIES timecop BUNDLED WITH - 1.16.1 + 1.16.2 diff --git a/README.md b/README.md index de2b5d2..ced8891 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,14 @@ class ApiAreaController < ApplicationController ... ``` +# Delayed Job activity monitoring + +To add monitoring of whether Delayed Job keeps picking up jobs or checking for new jobs, add the following line to an initializer: + +```ruby +require 'roqua/core_ext/delayed_job/activity_monitoring' +``` + # AppSignal Probes roqua-support contains one generic probe to monitor the current delayed job backlog count: `Roqua::Probes::DelayedJobProbe` diff --git a/lib/roqua/core_ext/delayed_job/activity_monitoring.rb b/lib/roqua/core_ext/delayed_job/activity_monitoring.rb new file mode 100644 index 0000000..49b2fe2 --- /dev/null +++ b/lib/roqua/core_ext/delayed_job/activity_monitoring.rb @@ -0,0 +1,32 @@ +require 'delayed_job' + +# +# Updates the modification time of the file $RAILS_ROOT/tmp/delayed_job_activity when +# relevant worker activity occurs. To be used for monitoring whether a DelayedJob +# worker is still picking up new work in a timely fashion. +# +# A file based approach is used so it is easy to add a health check to a Docker container +# with the following command: +# `find /app/tmp -mmin -$MAXIMUM_AGE_OF_FILE_IN_MINUTES -type f -print | grep delayed_job_activity` +# +module DelayedJobActivityMonitoring + def work_off(num = 100) + FileUtils.touch(Rails.root.join('tmp', 'delayed_job_activity')) + super(num) + end + + protected + + def reserve_job + super.tap do |job| + if job + FileUtils.touch( + Rails.root.join('tmp', 'delayed_job_activity'), + mtime: job.max_run_time.from_now + ) + end + end + end +end + +Delayed::Worker.send(:prepend, DelayedJobActivityMonitoring) if defined?(Delayed) && defined?(Delayed::Worker) diff --git a/spec/roqua/core_ext/delayed_job/activity_monitoring_spec.rb b/spec/roqua/core_ext/delayed_job/activity_monitoring_spec.rb new file mode 100644 index 0000000..1bd1e4a --- /dev/null +++ b/spec/roqua/core_ext/delayed_job/activity_monitoring_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' +require 'pathname' +require 'timecop' +require 'fakefs/safe' + +require 'roqua/core_ext/delayed_job/activity_monitoring' + +describe DelayedJobActivityMonitoring do + after do + Timecop.return + end + + def initialize_fake_rails_root + FileUtils.mkdir_p(File.join('/app', 'tmp')) + allow(Rails).to receive(:root).and_return(Pathname.new('/app')) + end + + let(:monitoring_filename) { Rails.root.join('tmp', 'delayed_job_activity').to_s } + + it 'creates a file' do + FakeFS.with_fresh do + initialize_fake_rails_root + expect { Delayed::Worker.new.work_off } + .to change { File.exist?(monitoring_filename) } + + expect(File.mtime(monitoring_filename)) + .to be_within(1.second).of(Time.now) + end + end + + it 'updates the file modification time' do + FakeFS.with_fresh do + initialize_fake_rails_root + + Delayed::Worker.new.work_off + + Timecop.travel(1.minute) + + expect { Delayed::Worker.new.work_off } + .to change { File.mtime(monitoring_filename) } + + expect(File.mtime(monitoring_filename)) + .to be_within(1.second).of(Time.now) + end + end + + describe 'when a job is reserved' do + let(:max_run_time) { 2.hours } + let(:job) do + double( + name: 'name', + id: 123, + queue: 'queue', + max_run_time: max_run_time, + invoke_job: nil, + destroy: nil + ) + end + + it 'creates a file with a modification time in the future' do + FakeFS.with_fresh do + initialize_fake_rails_root + + worker = Delayed::Worker.new + expect(Delayed::Job).to receive(:reserve).and_return(job) + + expect { worker.send(:reserve_and_run_one_job) } + .to change { File.exist?(monitoring_filename) } + + expect(File.mtime(monitoring_filename)) + .to be_within(1.second).of(max_run_time.from_now) + end + end + end +end -- GitLab