How to create unique delayed jobs - ruby-on-rails

I have a method like this one
def abc
// some stuff here
end
handle_asynchronously :abc, queue: :xyz
I want to create a delayed job for this only if there isn't one already in the queue.
I really feel like this should have an easy solution
Thanks!

I know this post is old but it hasn't been replied.
Delayed jobs does not provide a way to identify jobs. https://github.com/collectiveidea/delayed_job/issues/192
My suggestion is that your job could check if it still has to run when it is executing, for example, comparing to a database value, etc. Inserting jobs in the table should be quick and you might lose that if you start checking for a certain job in the queue.
If you still want to look for duplicates when enqueuing, this might help you.
https://gist.github.com/landovsky/8c505ecab41eb38fa1c2cd23058a6ae3

Related

Best practices to introduce trigger-like functionality in rails

I want to introduce triggers in my rails application. By trigger i mean, automatically perform some action if a certain event occur. For example, a trigger can be defined to send email notification whenever an order is placed.
There are lot of applications already providing this functionality, for example check this article About triggers and how they work.
My question is, what are the best practices to implement triggers in rails? Inline vs Asynchronous handling which is better?
Any kind of help is much appreciated. Thank you
What you're looking for is ActiveJob in Rails.
You'll first enqueue a Job when the trigger is to be applied -> then execute this trigger through a Job in a Worker.
For example: Say you want to notify Admin as soon as a Post is created, so you'll proceed something like this:
class Post < ApplicationRecord
...
after_create :notify_admin
...
private
def notify_admin
NotifyAdminJob.perform_later(id)
end
end
This way, no matter how complex or time dependent your code be in NotifyAdminJob this will not have any impact on your Model execution (create/update)

Record progress for long running ActiveJob

Based on this question How to reference active delayed_job within the actual job I'm using Delayed::Job with an additional progress text column to record progress of a long running task.
I'm now trying to update my code to use ActiveJob, so I've replaced def before with before_perform, but the job object passed to before_perform is not the same as the one passed to before. And quite rightly, because the queue adapter is configurable and may not always be :delayed_job.
So, given that the queue adapter is configurable, is there a correct way to access (read and write) the progress column in table delayed_jobs?
Thanks.

How to force Rails ActiveRecord to commit a transaction flush

Is it possible to force ActiveRecord to push/flush a transaction (or just a save/create)?
I have a clock worker that creates tasks in the background for several task workers. The problem is, the clock worker will sometimes create a task and push it to a task worker before the clock worker information has been fully flushed to the db which causes an ugly race condition.
Using after_commit isn't really viable due to the architecture of the product and how the tasks are generated.
So in short, I need to be able to have one worker create a task and flush that task to the db.
ActiveRecord uses #transaction to create a block that begins and either rolls back or commits a transaction. I believe that would help your issue. Essentially (presuming Task is an ActiveRecord class):
Task.transaction do
new_task = Task.create(...)
end
BackgroundQueue.enqueue(new_task)
You could also go directly to the #connection underneath with:
Task.connection.commit_db_transaction
That's a bit low-level, though, and you have to be pretty confident about the way the code is being used. #after_commit is the best answer, even if it takes a little rejiggering of the code to make it work. If it won't work for certain, then these two approaches should help.
execute uses async_exec under the hood which may or may not be what you want. You could try using the lower level methods execute_and_clear (or even exec_no_cache) instead.

How do I run delayed job inserts in the backgroud without affecting page load - Rails

I have an RoR application like posting answers to a question. If a user answers to a question, notification messages are sent to all the users, who watch-listed the question, who tracks the question and to the owner of the question. I am using delayed jobs for creating the notification messages. so, While creating answer, there are many inserts into delayed job table going on,which is slowing down the page load. It takes more time to redirect to the question show page after the answer is created.
Currently I am inserting into answers table using AJAX request. Is there any way to insert into delayed jobs table in background after the AJAX request completes?
As we have been trying to say in comments:
It sounds like you have something like:
User.all.each do |user|
user.delay.some_long_operation
end
This ends up inserting a lot of rows into delayed_jobs. What we are suggesting is to refactor that code into the delayed job itself, roughly:
def delayed_operation
User.all.each do |user|
user.some_long_operation
end
end
self.delay.delayed_operation
Obviously, you'll have to adapt that, and probably put the delayed_operation into a model library somewhere, maybe as a class method... but the point is to put the delay call outside the big query and loop.
I really advice doing this like that in a separate process. Why has the user to wait for those meta-actions? Stick to delivering a result page and only notifying your server something has to be done.
Create a separate model PostponedAction to build a list of 'to-do' actions. If you post an answer, add one PostponedAction to this database, with a parameter of the answer id. Then give the results back to the user.
Use a separate process (cron job), to read the PostponedAction items, and handle those. Mark them as 'handled' or delete on succesfull handling. This way, the user is not bugged by slow server processes.
Beside the email jobs you currently have, invent another type of job handling the creation of these jobs.
def email_all
User.all.each do |user|
user.delay.email_one()
end
end
def email_one
# do the emailing
end
self.delay.email_all()
This way the user action only triggers one insert before they see the response. You can also track individual jobs.

Long running tasks in Rails

I have a controller that generates HTML, XML, and CSV reports. The queries used for these reports take over a minute to return their result.
What is the best approach to run these tasks in the background and then return the result to the user? I have looked into Backgroundrb. Is there anything more basic for my needs?
You could look at using DelayedJob to perform those queries for you, and have an additional table called "NotificationQueue". When a job is finished (with its resultset), store the resultset and the User ID of the person who made that query in the NotificationQueue table. then on every page load (and, if you like, every 15-20 seconds), poll that database and see if there are any completed queries.
DelayedJob is really great because you write your code as if it wasn't going to be a delayed job, and just change the code to do the following:
#Your method
Query.do_something(params)
#Change to
Query.send_later(:do_something, params)
We use it all the time at work, and it works great.

Resources