How to cancel scheduled job with delayed_job in Rails?
disclaimer: I am not an expert user of delayed_job...
"Is there any call to remove particular job, or jobs related to specific model, instance, etc?"
Delayed::Job is just an ActiveRecord object so you can find and destroy any of those records. Depending on your use case this could be handled different ways. If someone is going to manually destroy them this could be handled through an admin interface in your web app.
# list all jobs
Delayed::Job.all
# find a job by id
job = Delayed::Job.find(params[:id])
# delete it
job.delete
if you need some out of process task deleting jobs by 'job type' you could loop through each one and delete it if it matches your job; try this in script/console
class MyJob < Struct.new(:some_value);
def perform
# ...
end
end
my_job = MyJob.new('xyz')
job = Delayed::Job.enqueue(my_job, 0, 1.hour.from_now)
job.name
# => "MyJob"
job.handler
# => "--- !ruby/struct:MyJob \nsome_value: xyz\n"
so given the above if you wanted to delete all jobs of type MyJob
Delayed::Job.all.each do |job|
if job.name == "MyJob" then
job.delete
end
end
this may or may not help for your situation? in many cases you might want to delete a MyJob but only where the :some_value attribute was 'abc' and not 'xyz'. In this case you might need to implement a 'display_name' on your MyJob object. job.name will use this if it exists
class MyJob < Struct.new(:user_id);
def perform
# ...
end
def display_name
return "MyJob-User-#{user_id}"
end
end
# store reference to a User
my_job = MyJob.new(User.first.id) # users.id is 1
job = Delayed::Job.enqueue(my_job, 0, 1.hour.from_now)
job.name
# => "MyJob-User-1"
job.handler
# => "--- !ruby/struct:MyJob \nuser_id: 1\n"
this way you could be more selective about which records to delete?
hopefully this gives you enough information on possible ways to handle it?
Active Job - How to cancel a scheduled Action Mailer job?
I decided to approach the problem differently. Instead of scheduling a job and destroying it, I'm just simply scheduling a job.
When the user sets up a reminder for three days in the future, a job is created, just as before. If the user removes the email reminder, the job will still run in three days but won't do anything. Here's an example setup:
# controllers/users_controller.rb
def schedule_reminder
# Get the user that scheduled the reminder
user = User.find_by_id(params[:id])
# Create a reminder for the user
reminder = user.reminders.create(active: true)
# Schedule the reminder to be sent out
SendReminderJob.set(wait_until: 3.days.from_now).perform_later(reminder.id)
end
def unschedule_reminder
reminder = Reminder.find_by_id(params[:reminder_id])
reminder.destroy
end
When the user schedules a reminder, def schedule_reminder
is executed. This method creates a reminder and schedules a job that will run in 3 days. It passes in the reminder's ID as an argument so the job can retrieve the reminder when it runs.
When the user deletes the reminder, def unschedule_reminder
is executed. This method finds the reminder and deletes it.
Here's what my SendReminderJob
job looks like:
# jobs/send_reminder_job.rb
class SendReminderJob < ActiveJob::Base
queue_as :default
def perform(*args)
# Get the reminder
# args.first is the reminder ID
reminder = Reminder.find_by_id(args.first)
# Check if the reminder exists
if !reminder.nil?
# Send the email to the user
end
end
end
When this job runs in three days, it checks to see if the reminder is still set. If it is, it sends out an email to the user. Otherwise, it does nothing. Regardless of whether or not it does anything, this job is deleted after three days!
delayed_job. Delete job from queue
Sounds like you want to de-queue any jobs if a radar object gets updated and re-queue.
Delayed::Job.enqueue should return a Delayed::Job record, so you can grab the ID off of that and save it back onto the Radar record (create a field for it on radar document) so you can find it again later easily.
You should change it to a before_save so you don't enter an infinite loop of saving.
before_save :post_on_facebook
def post_on_facebook
if self.user.settings.post_facebook && self.valid?
# delete existing delayed_job if present
Delayed::Job.find(self.delayed_job_id).destroy if self.delayed_job_id
# enqueue job
dj = Delayed::Job.enqueue(
::FacebookJob.new(self.user,self.body,url,self.title),0,self.active_from
)
# save id of delayed job on radar record
self.delayed_job_id = dj.id
end
end
Is it possible to terminate an already running delayed job using Ruby Threading?
It seems that my boss hit the spot so here's what we did(please tell us if there's some possibility this is bad practice so I can bring it up):
- First, we have a Job model that has def execute! (which runs what it's supposed to do).
- Next, we have delayed_job worker in the background, listening for new jobs. Now when you create a job, you can schedule it to run immediately or run every certain day (we use rufus for this one)
- When a job is created, it checks if its supposed to run immediately. If it is, it adds itself to the delayed job queue. The execute function creates a Thread, so each job has its own thread.
- User in the ui can see if a job is running(if there's a started_at and no finished_at). If it IS running, there's a button to cancel it. Canceling it just sets the job's canceled_at to Time.now.
- While the job is running it also checks itself if it has a canceled_at or if
Time.now
is >finished_at
. If so, kill the thread.
Voila! We've tested it for one job and it seems to work. Now the only problem is scaling...
If you see any problems with this please do so in the comments or give more suggestions if ever :) I hope this helps some one too!
Delayed Job: How to retrieve and update a scheduled job?
You can simply work on the Delayed::Job model (say activerecord if you use delayed_job_active_record)
The question is what job you want to retrieve, and how you take care of that.
No matter how you manage your jobs for later retrieval, you should use something like:
# submit the job and remember id
job_id_remember_for_later = Delayed::Job.enqueue(job).id
#...
# later
job = Delayed::Job.find_by_id(job_id_remember_for_later)
job.update_attributes(:run_at => new_time, :attempts => 0)
job.save
This code is surely unsafe, you need to check lock, etc.
Also note that you need to config delayed_job to keep failed jobs (deleted by default after max_attempts failures).
Delayed::Worker.destroy_failed_jobs = false
How to pause and resume delayed_job tasks in rails
There's, unfortunately, no built-in way to pause and resume jobs with delayed_job. See the following GitHub issue: https://github.com/collectiveidea/delayed_job/issues/58
This is not to say you can't accomplish this with additional work. You could have you job periodically check your database to see if it should wait before continuing. Defining how to accomplish "waiting" is another story. More than likely, you best bet is to design a way for the "pause" action to stop the current job and save its state. "Resuming" would then fire up another job that continues where it left off.
Related Topics
Changing Table Name at Query Run Time in a Rails Application
Weird Imoperfection in Ruby Blocks
Shell Out from Ruby While Setting an Environment Variable
Ruby: Why Does Puts Call To_Ary
How to Find Out Which Gem Has a Specific Dependency
Understanding Ruby .Class and .Ancestors Methods
How to Compare Versions in Ruby
Peer-To-Peer File Sharing with Web Sockets
Bundle Command Not Found. Bad Interpreter
How to Pass Parameter on 'Vagrant Up' and Have It in the Scope of Vagrantfile
Ruby: How to Iterate Over a Range, But in Set Increments
How to Return Early from a Rake Task
Tell Ruby Program to Wait Some Amount of Time
How to Invoke an Instance Method on a Ruby Module Without Including It
How to Convert a Ruby Hash So That All of Its Keys Are Symbols