Sidekiq Job Monitor
This gem allows you to easily monitor your jobs progression from your client.
This is often useful when the user has to wait for a specific job to complete to access some produced data.
The original use case was to allow users to see a "work in progress" modal while a big document was being generated, and then redirect them to download it when the job has completed.
The gem plugs into Sidekiq's job processor as a middleware to track the job progress and make it available through an JSON endpoint.
There's also a simple javascript client that allows you to hook into the complete and failed events.
Installation
Add to your Gemfile and bundle install
:
gem 'sidekiq-job_monitor'
Mount the engine in your routes.rb :
mount Sidekiq::JobMonitor::Engine => '/job-monitor', as: :job_monitor
Then load the js library by adding the following to your application.js
:
//= require sidekiq-job_monitor
Usage
The supported workflow for the gem is the following :
- The user requests a document that need time to be processed
- The server enqueues a sidekiq job in high priority queue and returns a waiting message in the form of a modal (for instance)
- The client polls the server to know when the job is done
- When the job is done, the server returns data to the client so that it can take some action to deliver the document
1. Requesting the document
Add a link to the job starting endpoint as a remote link :
<%= link_to 'Download report', build_report_path(@report), remote: true, data: { :'job-monitor-link' => true }
Initialize the javascript client and make it handle the main events :
$(function() {
$('[data-job-monitor-link]').each(function(i, el) {
$(el).sidekiqJobMonitor({
onStart: function($el) {
// If you return a modal box from the server, initialize it
$el.appendTo('body').modal();
// Handle the "complete" event and redirect the user to a target
// URL where it will be able to download the document
$el.on('complete', function(e, monitor, data) {
window.location.href = data.url;
});
// You can also stop monitoring the progress, for instance when the
// modal is closed :
$el.on('hide', function() {
$el.trigger('stop');
});
}
});
});
});
2. The server enqueues a sidekiq job in high priority queue and returns a waiting message
Enqueue a job with a worker as usual, and store the Job ID in the controller
class ReportsController
def build
@job_id = MyReportWorker.perform_async(params[:id])
render layout: false
end
end
Render a modal and return the monitoring URL as the main returned node's
[data-monitor-url]
attribute, with the URL returned by job_monitor.job_progress_path(job_id)
:
<div class="modal" data-monitor-url="<%= job_monitor.job_progress_path(@job_id) %>">
<!-- Snip -->
Please wait ...
<!-- Snip -->
</div>
3. The client polls the server to know when the job is done
This part is already covered by the javascript code we wrote in step 1.
All you need is the correct plugin initialization and returning the
data-monitor-url
attribute on the main returned node.
4. The client polls the server to know when the job is done
When the job is done, a javascript complete
event is triggered on the modal.
We already handled that in step 1.
All we need now is to provide the url
field in the data object returned by
the server upon job completion.
To do so, we add the #monitoring_data
method to our worker class.
This method takes the exact same arguments as the #perform
method, plus
the current job state (pending, processing, complete or failed) as the last
argument.
class MyReportWorker
include Sidekiq::Worker
def perform(report_id)
# Your worker logic here as usual ...
end
def monitoring_data(report_id, state)
# Pseudo logic to get the desired URL
{ url: Report.find(report_id).document_url } if state == 'complete'
end
end
Those fields will be added to the data hash returned by the server as JSON
and passed to the complete
event callback as the last argument.
Licence
This project rocks and uses MIT-LICENSE.