Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to retry a failed job just like resque-web providing a 'retry' button and recording retried? #324

Open
ouonet opened this issue Dec 29, 2016 · 1 comment

Comments

@ouonet
Copy link

ouonet commented Dec 29, 2016

how to retry a failed job just like resque-web providing a 'retry' button and recording retried?

@eric1234
Copy link

eric1234 commented Jan 20, 2017

I have a need to retry jobs as well although not manually via a web UI. I want to give a job X attempts with some time in between, then perm fail. To do this I created the following event hook. This looks for a property on the job class called retry_count. If defined, it will retry that many times with one hour between each attempt. If the property is not defined it will not retry.

This could be extended to do some sort of backoff delay instead of an hour each time. Also it assume the php-resque-scheduler is running as it uses this to queue the job in the future.

Putting this code here in case someone like me searching the repo for retry hoping for the desired functionality:

<?php
# Allows a job to be retried. Not all jobs are retryable. To indicate a job
# would like to be allowed to retry just assign a public static attribute named
# `retry_count` with a number of retries you would like to allow.
#
# Depends on php-resque-scheduler to run delayed tasks.
#
# NOTE: This retry mechanism is not 100% solid. There is a brief moment where
# the job is removed from the queue but the retry is not yet scheduled. If
# something goes wrong in those moments the job is lost. For our purposes that
# is good enough.


# Common code for both callbacks.
function if_retry($job, $callback) {
  # For easy reference
  $logger = $job->worker->logger;
  $job_class = $job->payload['class'];
  $args = $job->getArguments();

  # If there is no indication of previous retries but the class does indicate
  # they would like us to retry, initialize the retry_count
  if(!array_key_exists('retry_count', $args) && property_exists($job_class, 'retry_count'))
    $args['retry_count'] = $job_class::$retry_count;

  # Figure out the number of retries remaining.
  $args['retry_count'] -= 1;

  # See if they would like us to retry.
  if( array_key_exists('retry_count', $args) )
    call_user_func($callback, $job_class, $args, $logger);
}

# Pre-emptivly schedule retry under the assumption that a failure will occur
# (some failures like the parent process dying don't allow for detection).
Resque_Event::listen('afterFork', function($job) {
  if_retry($job, function($class, $args, $logger) use($job) {
    # If remaining then requeue in one hour
    if( $args['retry_count'] > 0 ) {
      $run_at = strtotime('1 hour');
      $logger->notice('Pre-emptively scheduling retry to re-run at {time}', ['time' => strftime('%c', $run_at)]);
      ResqueScheduler::enqueueAt($run_at, $job->queue, $class, $args);
    }
  });
});

# The job succeeded, remove the pre-emptive schedule
Resque_Event::listen('afterPerform', function($job) {
  if_retry($job, function($class, $args, $logger) use($job) {
    $logger->notice('Task succeeded, removing pre-emptive retry');
    ResqueScheduler::removeDelayed($job->queue, $class, $args);
  });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants