Update args of a scheduled job - ruby-on-rails

I have scheduled a job
Worker.perform_at(time, args)
And I can fetch the scheduled jobs
job = Sidekiq::ScheduledSet.new.find_job(jid)
job.args # this is the args I passed above
I need to update the args that will be passed to the worker when it is called, i.e. update job.args. How do I do that?
This won't work:
job.args = new_args
Sidekiq::ScheduledSet.new.to_a[0] = job

Well update the task is not the way achieving it cancel job and create new with new args:
job = Sidekiq::ScheduledSet.new.find_job(jid)
## time = job.time // Or just set time needed.
Sidekiq::Status.cancel jid
Worker.perform_at(time, new_args)
it will also make it easier for you to debug and log the jobs because when you edit/update them on the fly could cause bugs that very hard to identify.

Related

How to prevent Quartz.Net misfired job from retrying

I know I can specify .WithMisfireHandlingInstructionDoNothing() when building the trigger but some of my jobs are triggered via the IScheduler.TriggerJob() method, so without any triggers.
I can detect and log misfires in the ITriggerListener listener but how can I stop Quartz from trying to fire the job again? If I understand correctly .VetoJobExecution is not usable since the job has to be triggered successfully anyway.
Any other ideas?
Edit: my implementation
JobDataMap jobData = new JobDataMap(data);
IJobDetail jobTemplate = await jobScheduler.GetJobDetail(jobKey);
var jobTrigger = TriggerBuilder.Create()
.ForJob(jobTemplate)
.UsingJobData(jobData)
.WithSimpleSchedule(s => s.WithRepeatCount(0).WithMisfireHandlingInstructionNextWithRemainingCount())
.StartNow()
.Build();
await jobScheduler.ScheduleJob(jobTrigger);
Well if you just want the TriggerJob behavior you can achieve that just by adding one simple trigger to scheduler that is going to trigger immediately and configure retry policy for that. So if you can change the call sites of TriggerJob to create a simple trigger instead (maybe an extension method that allows to define the policy), the Quartz source for TriggerJob is here.
I think I've got it.
You have to use the WithMisfireHandlingInstructionNextWithRemainingCount policy, and WithRepeatCount(0).
But the trick here is to set the right value in the IScheduler MisfireThreshold to the misfire to be considered as a real misfire. I mean, if your misfire threshold is set to 60 seconds (default) then any job not executed in less than 60 seconds from schedule, will NOT be considered as a misfire. And so, the misfire policy will not be used.
In my case, with 3 second duration jobs, I had to set the WithMisfireThreshold to 1 second or less.
This way, the job is not retried if "really misfired".

Quartz.Net Trigger Scheduled Job On Demand

I have some Quartz.Net jobs which are running on a Schedule
scheduler.ScheduleJob(
new JobDetailImpl("MarkAsSolutionReminderJob", typeof(MarkAsSolutionReminderJob)),
new CalendarIntervalTriggerImpl("MarkAsSolutionReminderJobTrigger", IntervalUnit.Hour, 6));
Is it possible for me to manually trigger this Job to run when I want it to?
So it continues to run as normal, but in a specific piece of code I might want to just run it out of schedule once or twice. But it doesn't affect the scheduled job?
Is it possible for me to manually trigger this Job to run when I want it to?
Yes, you can trigger this job as and when you need.
Use void TriggerJob(JobKey jobKey) method for this as below:
scheduler.TriggerJob(new Jobkey("MarkAsSolutionReminderJob"));
If you want to pass some data to the job while executing it on demand, you can also do that by just using another overload void TriggerJob(JobKey jobKey, JobDataMap data); of the same method as below:
Dictionary<string, string> data = new Dictionary<string, string>();
//populate dictionary as per your needs
JobDataMap jobData = new JobDataMap(data);
scheduler.TriggerJob(new Jobkey("MarkAsSolutionReminderJob"),jobData);

Getting Cron Expressions from a Quartz Job

I was wondering if it possible to get the trigger cron expression from a Quartz Job.
I'm trying to do this for a few unit tests.
Gist from bassmartin's Answer
https://gist.github.com/jdgiotta/8c33402c1a026c2ccb12
Yes it's possible:
Inject the JobManagerService
def jobManagerService
Fetch the triggers of a job
List triggers = jobManagerService.quartzScheduler.getTriggersOfJob(JobKey.jobKey(MyJob.canonicalName, GrailsJobClassConstants.DEFAULT_GROUP))
Assuming your triggers are CronTrigger
triggers.each { trigger ->
println trigger.getCronExpression()
}

How to set max_run_time for a specific job?

I want to set Delayed::Worker.max_run_time = 1.hour for a specific job that I know will take a while. However, this is set as a global configuration in initializers/delayed_job_config.rb. As a result, this change will make ALL of my jobs have a max run time of 1 hour. Is there a way to just change it for one specific job without creating a custom job?
Looking at the Worker class on GitHub:
def run(job)
job_say job, 'RUNNING'
runtime = Benchmark.realtime do
Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job }
job.destroy
end
job_say job, 'COMPLETED after %.4f' % runtime
return true # did work
rescue DeserializationError => error
job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
failed(job)
rescue Exception => error
self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) }
return false # work failed
end
It doesn't appear that you can set a per-job max. But I would think you could roll your own timeout, in your job. Assuming the Timeout class allows nesting! Worth a try.
class MyLongJobClass
def perform
Timeout.timeout(1.hour.to_i, WorkerTimeout) { do_perform }
end
private
def do_perform
# ... real perform work
end
end
You can now set a per job max run time, but it must be lower than the global constant.
To set a per-job max run time that overrides the Delayed::Worker.max_run_time you can define a max_run_time method on the job
NOTE: this can ONLY be used to set a max_run_time that is lower than
Delayed::Worker.max_run_time. Otherwise the lock on the job would
expire and another worker would start the working on the in progress
job.
I have a parent Job class where I set max_run_time to 10 minutes. Then override that method for the one that I want to be really long. Then set the global constant to be really long as well.

How to set up a one-off job trigger at a specified time using Grails Quartz2 plugin

I am using Quartz2 Plugin, and am trying to dynamically trigger a very simple job. When the user performs a certain action, the job should be triggered some certain number of minutes in the future, and only run once.
I have tried using the simple 'schedule' method that takes a date and job data:
def sendTime = new Date()
use(groovy.time.TimeCategory) {
sendTime = sendTime + (connectionInstance.timeout).minutes
println "I will send the job at $sendTime"
}
ReportSmssyncTimeoutJob.schedule(sendTime, [connectionId:params.id])
In this setup, I find that the job actually triggers immediately instead of waiting until 'sendTime'.
My second attempt, after looking at the plugin source, was to use a SimpleTrigger
def sendTime = new Date()
use(groovy.time.TimeCategory) {
sendTime = sendTime + (connectionInstance.timeout).minutes
println "I will send the job at $sendTime"
}
// arguments here are: jobKey='test', startTime=sendTime, repeatCount=0, repeatInterval=1 (zero not allowed), job-arguments)
def trigger = TriggerHelper.simpleTrigger(new JobKey("test"), sendTime, 0, 1, [connectionId:params.id])
ReportSmssyncTimeoutJob.schedule(trigger)
In this setup, the job also triggers immediately. Is there something wrong with the SimpleTrigger implementation which prevents it from waiting until startDate?
Unfortunately, switching to the main 'quartz' plugin (which now has support for Quartz 2) is not an option as I am working on a project that has loads of jobs set up to work with the quartz2 plugin.
I asked this on the Grails mailing list and got the answer: this is a bug in the quartz2 plugin. It should be fixed in the next release (bug was noted in 0.2.3).
Update: tested this in v2.1.6.2 of the quartz2 plugin, and can confirm that both of the approaches in my question now work.

Resources