For example I have a job that I want recurrence every 1 hour. Here is my code:
class NewsletterJob < ApplicationJob
queue_as :default
after_perform do |job|
NewsletterJob.set(wait: 1.hour).perform_later user
end
def perform(*args)
puts "I am busy mailing newsletter."
end
end
My question is: How can I call above code that is satisfied 2 conditions:
run automatically when server is started.
run only once. For example, I use sidekiq for message queue. So I want the next time restart server, only one instance of job is available.
There are a list of cron projects here https://github.com/mperham/sidekiq/wiki/Related-Projects#recurring-jobs
Related
I would like to launch a job which is going to calculate the points of each user of my web-App.
Here is the problem, I would like to launch it automatically with sidekiq-scheduler.
But I have trouble to understand how I can launch my job with an argument which is going to change. I mean I have to calculate the amount of points for each user, so the argument is going to change and take different user_id.
Here is my code :
class PointsjoueurJob < ApplicationJob
queue_as :default
def perform(user_id)
#user = User.find(user_id)
#playerseason = PlayerSeason.where(user_id: #user.id)
#forecasts = Forecast.where(player_season_id: #playerseason)
points = []
#forecasts.each do |forecast|
if forecast.points_win.present? || forecast.points_lose.present?
if forecast.points_win.present?
points << forecast.points_win
else forecast.points_lose.present?
points << forecast.points_lose
end
#playerseason.update(number_of_points: points.sum)
else
end
end
end
Right now if I want to launch it, I have to go to my console then type :
PointsjoueurJob.perform_now(1)
But I want to schedule this with sidekiq-scheduler. The goal is to trigger the work everyday at 01h00 (cron: '0 1 * * *')but I don't know how to set-up the argument in order for the job to iterate trough all the users.
Thank you by advance.
Assuming that you want to recalculate all users' totals, you can create a separate 'wrapper' job, which is scheduled, that in turn enqueues the individual recalculation jobs:
class RecalcPointsJob < ApplicationJob
queue_as :default
def perform
User.find_each do |u|
PointsjoueurJob.perform_later(u.id)
end
end
end
If you are after a subset of users instead, substitute User.where() or User.find_by().
You can generate a Task and use whenever, then setup it.
on task you can write this:
rails g task test cron
namespace :test do
task :cron do
User.find_each do |u|
PointsjoueurJob.perform_async(u.id)
end
end
end
then in config/schedule.rb after install whenever
every '0 1 * * *' do
rake "test:cron"
end
then
whenever --update-crontab
How i can see the list scheduled or pending active job in rails 5.2.
I have Scheduled a job after 10min to send a email but user resend it again and again so i need to see that is job scheduled is executed or not.
My Job look like this
class GenerateTemplatePreviewJob < ApplicationJob
queue_as :default
def perform(*args)
# Do something later
end
end
I Have Called The job from Model
after_save ->{
GenerateTemplatePreviewJob.set(wait_until:
10.second.after).perform_later(self.template.id.to_s)
}
I have a job created with rails g job cleanUp.
Is where any option to check if Job is running? Something like this CleanUpJob.isRunning?
If where is no way to make it without additional gems, which will be the simplest? delayed_job?
Second thing to control Job is progress, any thoughts how to implement CleanUpJob.progress or progress_job should be my choice?
briefly:
I need to create a job with two methods (isRunning?, progress).
I don't really want additional tables if possible.
You can use Class: Sidekiq::ScheduledSet for this purpose.
Documentation here
This Class is used in Sidekiq web interface.
Example:
Save job id (jid) when set job. Then you can call it for queried instance
def is_running?
require 'sidekiq/api'
ss = Sidekiq::ScheduledSet.new
jobs = ss.select {|ret| ret.jid == self.jid}
jobs.any?
end
Or, you can set DB flag inside Job with around_perform hook.
class SomeJob < ActiveJob::Base
queue_as :some_job
around_perform do |job, block|
start_import_process_log job.arguments[0], job.arguments[1] || {}
block.call
finish_import_process_log
end
# ...
private
def start_import_process_log import_process, options={}
#some actions
end
def finish_import_process_log
end
end
In this example associated log record is created.
Or you can use before_perform/ after_perform.
In my practice I'm using creting log records on long tasks.
When I need to find and kill job as example - I'm using Sidekiq::ScheduledSet.
I have a Rails app with 2 jobs (ImportCsvJob and ProcessCsvJob). So that I can visibly hint in the app that there are still jobs in the queue I have this helper method (inside application_helper.rb):
module ApplicationHelper
def queued_job_count
Sidekiq::Queue.new.size
end
end
Then I use it on my index controller which is then passed to the view for processing and giving the visual hint in the app.
def index
#still_have_jobs = !queued_job_count.zero?
end
However, this works when I still had 1 background Job (ImportCsvJob), but when I added the (ProcessCsvJob) it does not work anymore.
import_csv_job.rb
require 'open-uri'
class ImportCsvJob < ActiveJob::Base
queue_as :default
def perform(csv_record)
csv_record[:object_changes] = ApplicationController.helpers.generate_hash(csv_record[:object_changes])
ObjectRecord.create(csv_record)
end
end
process_csv_job.rb
class ProcessCsvJob < ActiveJob::Base
queue_as :default
def perform(csv_path)
csv_file = open(csv_path,'rb:UTF-8')
options = {
row_sep: :auto, col_sep: ",",
user_provided_headers: [:object_id, :object_type, :timestamp, :object_changes],
remove_empty_values: true,
headers_in_file: true
}
SmarterCSV.process(csv_file, options) do |array|
ImportCsvJob.perform_later(array.first)
end
end
end
and lastly, in the model where this is called:
ProcessCsvJob.perform_later(gdrive.uploaded_file_link)
When I try to debug in Rails console using Sidekiq::Queue.new.size, it still gives out 0.
Running:
redis-server
bundle exec sidekiq
A job that is executing is not enqueued anymore. The Sidekiq process has already popped it off the queue and is executing it. The queue is empty but the job is not finished yet.
So, basically I added a monitoring for sidekiq using the web interface to see what was happening:
And as I inspected, there were no enqueued tasks nor scheduled since most of the job is set to perform almost immediately (on parallel).
Thus here's my solution to know if the count of busy jobs:
module ApplicationHelper
def queued_job_count
Sidekiq::ProcessSet.new.first['busy']
end
end
and then on the index:
def index
#still_have_jobs = !queued_job_count.zero?
end
it works! :)
A Rails app has a background job that calls a service object as below
class ObjectUpdateJob < ActiveJob::Base
queue_as :default
def perform(object_id)
ObjectUpdater.call(object_id)
end
end
class ObjectUpdater
def self.call(*args)
if #object = Object.find( args[:object_id] )
#... update some stuff from external services
else
#... raise an error and prevent the background job from attempting to retry
end
end
end
This may be a simple question, but my experience with error handling is limited and I'd appreciate a second thought on this.
What is the 'Rails way' of handling this 'record does not exist' case, and ensuring the background job is informed so that no retries are attempted?
You could just put a guard clause into worker's perform action:
class ObjectUpdateJob < ActiveJob::Base
queue_as :default
def perform(object_id)
return unless Object.find(object_id)
ObjectUpdater.call(object_id)
end
end
But for more protection you can put some check to the job call:
ObjectUpdateJob.perform_now(object_id) if Object.find(object_id)
This way you just won't let the inexistent object to be processed.
If you use Sidekiq you can set a flag to :retry => false so that jobs do not retry automatically.
Additionally Sidekiq has built in error handling mechanism. Here is more information regarding the matter: https://github.com/mperham/sidekiq/wiki/Error-Handling
Hope that helps.