Esta página describe cómo configurar la reintentación de tareas.

Comportamiento predeterminado

Por defecto, asynq reintentará una tarea un máximo de 25 veces. Cada vez que se reintenta la tarea, utiliza una estrategia de retroceso exponencial para calcular el retraso de reintento. Si una tarea agota todos sus intentos de reintento (el valor predeterminado es 25 veces), la tarea se moverá al estado archivado con fines de depuración e inspección, y no se reintentará automáticamente (aún puede ejecutar la tarea manualmente mediante la CLI o WebUI).

Se pueden personalizar las siguientes propiedades de reintento de tarea:

  • Número máximo de intentos de reintento para cada tarea
  • Intervalo de tiempo para esperar antes de que una tarea fallida pueda reintentarse (es decir, retraso)
  • Si usar el contador de reintento de la tarea
  • Si omitir reintentos y enviar la tarea directamente al archivo

Las partes restantes de esta página describen cada opción personalizada mencionada anteriormente.

Personalizar el número máximo de intentos de reintento para las tareas

Puede especificar el número máximo de intentos de reintento para una tarea al encolar la tarea utilizando la opción asynq.MaxRetry.

Ejemplo:

client.Enqueue(task, asynq.MaxRetry(5))

Esto indica que la task debe ser reintentada como máximo cinco veces.

Alternativamente, si desea establecer el número máximo de intentos de reintento para una tarea en particular, puede configurarlo como opción predeterminada para la tarea.

task := asynq.NewTask("feed:import", nil, asynq.MaxRetry(5))
client.Enqueue(task) // MaxRetry establecido en 5

Personalizar el retraso de reintento

Puede especificar cómo calcular el retraso de reintento utilizando la opción RetryDelayFunc en la estructura Config.

La firma de RetryDelayFunc es la siguiente:

// n es el número de veces que se ha reintentado la tarea
// e es el error devuelto por el manejador de tareas
// t es la tarea relacionada
RetryDelayFunc func(n int, e error, t *asynq.Task) time.Duration

Ejemplo:

srv := asynq.NewServer(redis, asynq.Config{
    Concurrency: 20,
    RetryDelayFunc: func(n int, e error, t *asynq.Task) time.Duration {
        return 2 * time.Second
    },
})

Esto indica que todas las tareas fallidas esperarán dos segundos antes de ser procesadas nuevamente.

El comportamiento predeterminado es retroceso exponencial, definido por DefaultRetryDelayFunc. El siguiente ejemplo muestra cómo personalizar el retraso de reintento para tipos de tarea específicos:

srv := asynq.NewServer(redis, asynq.Config{
    // Para las tareas "foo", siempre utilizar un retraso de 2 segundos, las otras tareas usan el comportamiento predeterminado.
    RetryDelayFunc: func(n int, e error, t *asynq.Task) time.Duration {
        if t.Type() == "foo" {
            return 2 * time.Second 
        }
        return asynq.DefaultRetryDelayFunc(n, e, t) 
    },
})

Error de no falla

A veces, es posible que desee devolver un error del manejador y reintentar la tarea más tarde, pero sin usar el contador de reintento de la tarea. Por ejemplo, es posible que desee reintentar más tarde porque la unidad de trabajo no tiene suficientes recursos para manejar la tarea. Durante la inicialización del servidor, puede optar por proporcionar un Config para la función IsFailure(error) bool. Esta función predicado determina si el error del manejador cuenta como una falla. Si la función devuelve false (es decir, un error de no falla), el servidor no utilizará el contador de reintento de la tarea y simplemente programará la tarea para reintento posterior.

Ejemplo:

var ErrResourceNotAvailable = errors.New("no hay recursos disponibles")

func HandleResourceIntensiveTask(ctx context.Context, task *asynq.Task) error {
    if !IsResourceAvailable() {
        return ErrResourceNotAvailable
    }
    // ... lógica para manejar tarea intensiva de recursos
}

// ...

srv := asynq.NewServer(redisConnOpt, asynq.Config{
    // ... otras opciones de configuración
    IsFailure: func(err error) bool {
        return err != ErrResourceNotAvailable // error de no falla si no hay recursos disponibles
    },
})

Omitir reintento

Si Handler.ProcessTask devuelve un error SkipRetry, la tarea será archivada independientemente del número de intentos restantes de reintento. El error devuelto puede ser SkipRetry o un error envuelto con el error SkipRetry.

func ExampleHandler(ctx context.Context, task *asynq.Task) error {
    // Lógica de manejo de tarea aquí...
    // Si el manejador sabe que la tarea no debe reintentarse, devuelve SkipRetry
    return fmt.Errorf(": %w", asynq.SkipRetry)
}