Delayed Job Queue, Priority, Worker - ruby-on-rails

I have around 10 queues in my code, each having separate workers. All have default priorities - 1.
Currently, the requests coming from user (UI) & rake tasks are going in same queue. Suppose, request coming for fetching data from user & rake task are going in same queue with priority 1 & both are executing one by one depends on request time.
But I want to run the request from user first & then the rake task. How can I manage this ?
I also want to understand if I create a separate queue for rake task & give it low priority, then how it will work. Will it run along with another queues or wait for them to execute first?
Delayed::Worker.queue_attributes = {
high_priority: { priority: -10 },
low_priority: { priority: 10 }
}

This article could help you further - it also has coding examples that you can use to try with your own code:
https://phil.tech/2016/tips-on-sidekiq-queues/
Basically, if you want to run the request from users first, you should make a queue for them and assign it a higher priority. No need to assign -10, just start from 0 and work your way up.
But also keep in mind that you should not have more than a handful queues, so it might make sense to bundle different priorities together:
I don't recommend having more than a handful of queues. Lots of queues makes for a more complex system and Sidekiq Pro cannot reliably handle multiple queues without polling. M Sidekiq Pro processes polling N queues means O(M*N) operations per second slamming Redis.
https://github.com/mperham/sidekiq/wiki/Advanced-Options#queues
Finally, if one part of your code has to run before another, you could also enqueue the rake task from the job that handles the user requests.

But I want to run the request from user first & then the rake task.
How can I manage this ?
Recommend change Delayed::Worker.default_priority to a positive number, for example: 5, then define a priority method in the user workers return a lower number
class UserRequestJob < ApplicationJob
def priority
1 # lower numbers run first
end
...
end
I also want to understand if I create a separate queue for rake task &
give it low priority, then how it will work. Will it run along with
another queues or wait for them to execute first?
It will run higher priority jobs first, and then lower priority jobs, in fact queue name doesn't matter after a job is enqueued, unless you have dedicated workers, when you define queues with priority:
Delayed::Worker.queue_attributes = {
high_priority: { priority: -10 },
low_priority: { priority: 10 }
}
and then you enqueue a job to a queue, delayed_job will get the priority by queue name, as you can see in job_preparer.rb
References:
https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html
https://github.com/collectiveidea/delayed_job#named-queues

Related

Delayed Job One worker for one task (synchronous call)

I have 20 Delayed job workers
I had search so many links and post for synchronous processing for that job but nothing works for me
Also assign QUEUE for perticular worker like this
script/delayed_job --queue=order_item_kit start -i 0
order.delay(:queue => "order_item_kit").update_order
But this will assign to this queue and other worker also works for it.
I am searching for one worker will run one job at a time.
for this particular case
Please give suggestion to work for this case, one method calls only one worker as a time.
Thanks in advance.
I can solve this by doing that way
When it assign next same job to another worker, we can add run_at one second ahead, so multiple workers does not save object same time and this fix our issue
latest_job = Delayed::Job.where(queue: "order_item_kit").last
run_at = latest_job ? latest_job.run_at + 1.seconds : 1.seconds.from_now
kit_sku.delay(:run_at => run_at,:queue =>"order_item_kit").update_ordere_item
According to the DJ documentation
Processes started without specifying a queue will run jobs from any queue.
You should run your workers with explicit queue name:
RAILS_ENV=production script/delayed_job --queue=tracking start
And with some other queue name for all other ('default') workers.
To define that name set Delayed::Worker.default_queue_name in initializer.

Sidekiq - how to execute the job immediately (+ does make sense to use a queue in this case)?

I have a task that I need to generate immediately after the request is created and get it done ASAP.
So for this purpose, I have created a /config/sidekiq.yml file where I defined this:
---
:queues:
- default
- [critical, 10]
And for the respective worker, I set this:
class GeneratePDFWorker
include Sidekiq::Worker
sidekiq_options queue: 'critical', retry: false
def perform(order_id)
...
Then, when I call this worker:
GeneratePDFWorker.perform_async(#order.id)
So I am testing this. But - I found this post, where is said that if I want to execute the tasks immediately, I should call:
GeneratePDFWorker.new.perform(#order.id)
So my question is - should I use the combination of a (critical) queue + the new (GeneratePDFWorker.new.perform) method? Does it make sense?
Also, how can I verify that the tasks is execute as critical?
Thank you
So my question is - should I use the combination of a (critical) queue + the new (GeneratePDFWorker.new.perform) method? Does it make sense?
Using GeneratePDFWorker.new.perform will run the code right there and then, like normal, inline code (in a blocking manner, not async). You can't define a queue, because it's not being queued.
As Walking Wiki mentioned, GeneratePDFWorker.new.perform(#order.id) will call the worker synchronously. So if you did this from a controller action, the request would block until the perform method completed.
I think your approach of using priority queues for critical tasks with Sidekiq is the way to go. As long as you have enough Sidekiq workers, and your queue isn't backlogged, the task should run almost immediately so the benefit of running your worker in-process is pretty much nil. So I'd say yes, it does make sense to queue in this case.
Also, you're probably aware of this, but sidekiq has a great monitoring UI: https://github.com/mperham/sidekiq/wiki/Monitoring. This should should make it easy to get reliable, detailed metrics on the performance of your workers.
should I use the combination of a (critical) queue?
Me:
Yes you can use critical queue if you feel so. A queue with a weight of 2 will be checked twice as often as a queue with a weight of 1.
Tips:
Keep the number of queues fewer as possible. Sidekiq is not designed to handler tremendous number of queues.
Also keep weights as simple as possible. If you want queues always processed in a specific order, just declare them in order without weights.
the new (GeneratePDFWorker.new.perform) method?
Me: No, using sidekiq in the same thread asynchronously is bad in the first place. This will hamper your application's performance as your application-server will be busy for longer. This will be very expensive for you. Then what will be the point of using sidekiq?

How to Wait for Parallel Sidekiq Workers to All Complete Before Calling Another Method?

I have several workers that are being run using Sidekiq and scheduled using Sidetiq. I'm looking for advice on the best way to wait for all workers to complete before executing a callback, similar to Sidekiq-Pro's batching functionality. Any advice on possible options would be greatly appreciated!
You can write a method:
def wait_for_sidekiq
sleep(1) until Sidekiq::Workers.new.size == 0 && Sidekiq::Queue.new.size == 0
end
I would also suggest that you make sure that the jobs got queued in the first place:
def wait_for_queuing
sleep(1) until Sidekiq::Queue.new.size > 0 || Sidekiq::Workers.new.size > 0
end
This is required, because sometimes the first method might get executed a few miliseconds before queuing the jobs, so that it won't wait at all.
For simple use cases you can poll stats to get a count of pending jobs for each queue.
So if you know that all your jobs go to a specific queue, and there are no other jobs going in that queue from elsewhere this would suffice. For more advanced/complex use cases you can refer the API source
Another simple solution is to use a Redis based counter (since you are already using Redis) and atomically decrement it from each job, and publish an event once the count reaches zero.
You should see this Sidekiq-batch gem, i already try it and it works just like the Sidekiq's Pro Batch, and it's free
Hope this could help

How can I get notified when Sidekiq Enqueues jobs or has stopped processing jobs?

I am on Heroku and I got an error because my redis db got too full. The my sidekiq processes stopped working. It was like that for a day until I realized it. Now I have 600+ jobs that I have tried to process but they are just breaking everything now. How can I sound off the alarms when sidekiq can't process jobs or when the Enqueue starts to fill up?
You could set a rake task on a schedule to check Sidekiq stats, and then take the appropriate action ( like send an email ).
I've created my own module with helper methods for Sidekiq that serves a number of purposes, e.g deleting jobs, checking queues, retriving jobs by certain criteria etc. https://gist.github.com/blotto/10324119
For your purpose, grab the sidekiq stats as such :
def sidekiq_stats()
summary = Hash.new
stats = Sidekiq::Stats.new
summary = { processed: stats.processed,
failed: stats.failed,
enqueued: stats.enqueued,
queues: stats.queues}
end
And then evaluate the enqueued value, set a tolerance on what you think is too high, and then let loose the hounds.
If you're using zabbix for monitoring, you could use sidekiq_queue_zabbix template at https://github.com/hungntit/sidekiq_queue_zabbix. This template supports showing graph and sending alert when sidekiq queue size is higher than one specified limit number

Moving a Resque job between queues

Is there anyway to move a resque job between two different queues?
We sometimes get in the situation that we have a big queue and a job that is near the end we find a need to "bump up its priority." We thought it might be an easy way to simply move it to another queue that had a worker waiting for any high priority jobs.
This happens rarely and is usually a case where we get a special call from a customer, so scaling, re-engineering don't seem totally necessary.
There is nothing built-in in Resque. You can use rpoplpush like:
module Resque
def self.move_queue(source, destination)
r = Resque.redis
r.llen("queue:#{source}").times do
r.rpoplpush("queue:#{source}", "queue:#{destination}")
end
end
end
https://gist.github.com/rafaelbandeira3/7088498
If it's a rare occurrence you're probably better off just manually pushing a new job into a shorter queue. You'll want to make sure that your system has a way to identify that the job has already run and to bail out so that when the job in the long queue is finally reached it is not processed again (if double processing is a problem for you).

Resources