

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Reintento de actividades con errores
<a name="features-retry"></a>

En ocasiones se producen errores en las actividades por motivos efímeros, como por ejemplo la pérdida temporal de conexión. En otras ocasiones, la actividad podría tener éxito, por lo que la manera apropiada de abordar el error en la actividad consiste con frecuencia en reintentar la actividad, quizás varias veces. 

Hay diferentes estrategias para reintentar actividades; la mejor depende de los detalles de su flujo de trabajo. Las estrategias se dividen en tres categorías básicas: 
+ La retry-until-success estrategia simplemente sigue reintentando la actividad hasta que se complete.
+ La estrategia de reintento exponencial aumenta el intervalo de tiempo entre reintentos exponencialmente hasta que se completa la actividad o el proceso alcanza un punto de parada especificado, como por ejemplo un número máximo de intentos.
+ La estrategia de reintento personalizada decide si se reintenta la actividad, o cómo hacerlo, después de cada intento en el que se ha producido un error.

Las siguientes secciones describen cómo implementar estas estrategias. Todos los procesos de trabajo de flujo de trabajo de ejemplo utilizan una sola actividad, `unreliableActivity`, que hace lo siguiente de manera aleatoria: 
+ Se completa de manera inmediata
+ Produce un error de manera intencionada superando el valor del tiempo de espera
+ Produce un error de manera intencionada produciendo una `IllegalStateException` 

## Retry-Until-Success Estrategia
<a name="features-retry-success"></a>

La estrategia de reintento más sencilla consiste en seguir reintentando la actividad cada vez que se produce un error hasta que finalmente se ejecuta satisfactoriamente. Este es el patrón básico:

1. Implemente una clase `TryCatch` o `TryCatchFinally` anidada en el método de punto de entrada de su flujo de trabajo.

1. Ejecute la actividad en `doTry`

1. Si se produce un error en la actividad, el marco de trabajo llama a `doCatch`, que ejecuta de nuevo el método de punto de entrada.

1. Repita los pasos 2 y 3 hasta que la actividad se realiza correctamente.

El siguiente flujo de trabajo implementa la retry-until-success estrategia. La interfaz de flujo de trabajo se implementa en `RetryActivityRecipeWorkflow` y tiene un método, `runUnreliableActivityTillSuccess`, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en `RetryActivityRecipeWorkflowImpl`, de la siguiente manera: 

```
public class RetryActivityRecipeWorkflowImpl
    implements RetryActivityRecipeWorkflow {

    @Override
    public void runUnreliableActivityTillSuccess() {
        final Settable<Boolean> retryActivity = new Settable<Boolean>();

        new TryCatch() {
            @Override
            protected void doTry() throws Throwable {
                Promise<Void> activityRanSuccessfully
                    = client.unreliableActivity();
                setRetryActivityToFalse(activityRanSuccessfully, retryActivity);
            }

            @Override
            protected void doCatch(Throwable e) throws Throwable {
                retryActivity.set(true);
            }
        };
        restartRunUnreliableActivityTillSuccess(retryActivity);
    }

    @Asynchronous
    private void setRetryActivityToFalse(
            Promise<Void> activityRanSuccessfully,
            @NoWait Settable<Boolean> retryActivity) {
        retryActivity.set(false);
    }

    @Asynchronous
    private void restartRunUnreliableActivityTillSuccess(
            Settable<Boolean> retryActivity) {
        if (retryActivity.get()) {
            runUnreliableActivityTillSuccess();
        }
    }
}
```

El flujo de trabajo funciona de la siguiente manera: 

1. `runUnreliableActivityTillSuccess` crea un objeto `Settable<Boolean>` denominado `retryActivity` que se utiliza para indicar si se ha producido un error en la actividad y debería volver a intentarse. `Settable<T>` proviene de `Promise<T>` y funciona de forma muy parecida, pero en este caso, usted establece manualmente el valor de un objeto `Settable<T>`.

1. `runUnreliableActivityTillSuccess` implementa una clase `TryCatch` anidada de manera anónima para gestionar cualquier excepción lanzada por la actividad `unreliableActivity`. Para obtener más información sobre cómo tratar excepciones lanzadas por un código asíncrono, consulte [Gestión de errores](errorhandling.md).

1. `doTry` ejecuta la actividad `unreliableActivity`, que devuelve un objeto `Promise<Void>` denominado `activityRanSuccessfully`.

1. `doTry` llama al método asíncrono `setRetryActivityToFalse`, que tiene dos parámetros:
   + `activityRanSuccessfully` toma el objeto `Promise<Void>` devuelto por la actividad `unreliableActivity`.
   + `retryActivity` toma el objeto `retryActivity`.

   Si `unreliableActivity` finaliza, `activityRanSuccessfully` está listo y `setRetryActivityToFalse` establece `retryActivity` en "false". De lo contrario, `activityRanSuccessfully` nunca está listo y `setRetryActivityToFalse` no se ejecuta.

1. Si `unreliableActivity` genera una excepción, el marco de trabajo llama a `doCatch` y le pasa el objeto de excepción. `doCatch` establece `retryActivity` en true.

1. `runUnreliableActivityTillSuccess` al método asíncrono `restartRunUnreliableActivityTillSuccess` y le pasa el objeto `retryActivity`. Dado que `retryActivity` es un tipo de `Promise<T>`, `restartRunUnreliableActivityTillSuccess` aplaza la ejecución hasta que `retryActivity` esté listo, lo que ocurre después de que se completa `TryCatch`. 

1. Cuando `retryActivity` está listo, `restartRunUnreliableActivityTillSuccess` extrae el valor.
   + Si el valor es `false`, el reintento ha tenido éxito. `restartRunUnreliableActivityTillSuccess` no hace nada y la secuencia de reintento termina.
   + Si el valor es "true", se ha producido un error en el reintento. `restartRunUnreliableActivityTillSuccess` llama a `runUnreliableActivityTillSuccess` para ejecutar de nuevo la actividad.

1. Los pasos 1 al 7 se repiten hasta que se completa `unreliableActivity`. 

**nota**  
`doCatch` no gestiona la excepción; simplemente establece el objeto `retryActivity` en "true" para indicar que se ha producido un error en la actividad. El reintento es gestionado por el método asíncrono `restartRunUnreliableActivityTillSuccess`, que aplaza la ejecución hasta que se completa `TryCatch`. El motivo de este enfoque es que, si reintenta una actividad en `doCatch`, no es posible cancelarla. Volver a intentar la actividad `restartRunUnreliableActivityTillSuccess` le permite ejecutar actividades que se pueden cancelar. 

## Estrategia de reintento exponencial
<a name="features-retry-exponential"></a>

Con la estrategia de reintento exponencial, el marco de trabajo ejecuta una actividad en la que se ha producido un error de nuevo tras un periodo de tiempo especificado, N segundos. Si se produce un error en ese intento, el marco de trabajo ejecuta de nuevo la actividad después de 2N segundos y luego tras 4N segundos, etc. Debido a que el tiempo de espera puede aumentar bastante, habitualmente interrumpe los reintentos en algún punto en lugar de continuar de manera indefinida.

El marco de trabajo ofrece tres maneras de implementar una estrategia de reintento exponencial:
+ La anotación `@ExponentialRetry` es el enfoque más sencillo, pero debe establecer las opciones de configuración de los reintentos en tiempo de compilación.
+ La clase `RetryDecorator` le permite establecer la configuración de reintentos en el tiempo de ejecución y cambiarla según sea necesario.
+ La clase `AsyncRetryingExecutor` le permite establecer la configuración de reintentos en el tiempo de ejecución y cambiarla según sea necesario. Además, el marco de trabajo llama a un método `AsyncRunnable.run` implementado por el usuario para la ejecución de cada reintento.

Todos los enfoques admiten las siguientes opciones de configuración, en las que los valores de tiempo se muestran en segundos: 
+ El tiempo de espera de reintento inicial.
+ El coeficiente de retardo, que se utiliza para computar los intervalos de reintento, de la siguiente manera:

  ```
  retryInterval = initialRetryIntervalSeconds * Math.pow(backoffCoefficient, numberOfTries - 2)
  ```

  El valor predeterminado es 2.0.
+ El número máximo de reintentos. El valor predeterminado es ilimitado.
+ El intervalo máximo de reintentos. El valor predeterminado es ilimitado.
+ El plazo de vencimiento. Los reintentos se detienen cuando la duración total del proceso supera este valor. El valor predeterminado es ilimitado.
+ Las excepciones que dispararán el proceso de reintento. De manera predeterminada, todas las excepciones disparan el proceso de reintento.
+ Las excepciones que no dispararán un reintento. De manera predeterminada, no se excluye ninguna excepción.

En las siguientes secciones se describen las distintas maneras de implementar una estrategia de reintento exponencial.

### Reintento exponencial con @ ExponentialRetry
<a name="features-retry-exponential-annotation"></a>

La manera más sencilla de implementar una estrategia de reintento exponencial para una actividad consiste en aplicar una anotación `@ExponentialRetry ` a la actividad en la definición de interfaz. Si se produce un error en la actividad, el marco de trabajo gestiona el proceso de reintento automáticamente, en función de los valores de opciones especificados. Este es el patrón básico:

1. Aplique `@ExponentialRetry` a las actividades apropiadas y especifique la configuración de reintento.

1. Si se produce un error en una actividad anotada, el marco de trabajo reintenta automáticamente la actividad en función de la configuración especificada por los argumentos del comentario. 

El proceso de trabajo del flujo de trabajo `ExponentialRetryAnnotationWorkflow` implementa la estrategia de reintento exponencial utilizando una anotación `@ExponentialRetry`. Utiliza una actividad `unreliableActivity` cuya definición de interfaz se implementa en `ExponentialRetryAnnotationActivities`, de la siguiente manera:

```
@Activities(version = "1.0")
@ActivityRegistrationOptions(
    defaultTaskScheduleToStartTimeoutSeconds = 30,
    defaultTaskStartToCloseTimeoutSeconds = 30)
public interface ExponentialRetryAnnotationActivities {
    @ExponentialRetry(
        initialRetryIntervalSeconds = 5,
        maximumAttempts = 5,
        exceptionsToRetry = IllegalStateException.class)
    public void unreliableActivity();
}
```

Las opciones de `@ExponentialRetry` especifican la siguiente estrategia: 
+ Reintentar solo si la actividad lanza `IllegalStateException`.
+ Usar el tiempo de espera inicial de 5 segundos.
+ No más de 5 reintentos. 

La interfaz de flujo de trabajo se implementa en `RetryWorkflow` y tiene un método, `process`, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en `ExponentialRetryAnnotationWorkflowImpl`, de la siguiente manera: 

```
public class ExponentialRetryAnnotationWorkflowImpl implements RetryWorkflow {
    public void process() {
        handleUnreliableActivity();
    }

    public void handleUnreliableActivity() {
        client.unreliableActivity();
    }
}
```

El flujo de trabajo funciona de la siguiente manera: 

1. `process` ejecuta el método síncrono `handleUnreliableActivity`.

1. `handleUnreliableActivity` ejecuta la actividad `unreliableActivity`. 

Si se produce un error en la actividad y lanza `IllegalStateException`, el marco de trabajo ejecuta automáticamente la estrategia de reintento especificada en `ExponentialRetryAnnotationActivities`.

### Reintento exponencial con la clase RetryDecorator
<a name="features-retry-exponential-decorator"></a>

`@ExponentialRetry` es fácil de utilizar. No obstante, la configuración es estática y se establece en el tiempo de compilación, por lo que el marco de trabajo utiliza la misma estrategia de reintento cada vez que se produce un error en la actividad. Puede implementar una estrategia de reintento exponencial más flexible utilizando la clase `RetryDecorator` que le permite especificar la configuración en el tiempo de ejecución y cambiarla según sea necesario. Este es el patrón básico:

1. Cree y configure un objeto `ExponentialRetryPolicy` que especifique la configuración de reintento.

1. Cree un objeto `RetryDecorator` y pase el objeto `ExponentialRetryPolicy` del Paso 1 al constructor.

1. Aplique el objeto decorador a la actividad pasando el nombre de la clase del cliente de la actividad al método de decoración del objeto `RetryDecorator`.

1. Ejecute la actividad.

Si se produce un error en la actividad, el marco de trabajo reintenta la actividad en función de la configuración del objeto `ExponentialRetryPolicy`. Puede cambiar la configuración de los reintentos según sea necesario modificando este objeto. 

**nota**  
La anotación `@ExponentialRetry` y la clase `RetryDecorator` se excluyen mutuamente. No puede utilizar `RetryDecorator` para anular dinámicamente una política de reintentos especificada por una anotación `@ExponentialRetry`. 

La siguiente implementación de flujo de trabajo muestra cómo usar la clase `RetryDecorator` para implementar una estrategia de reintento exponencial. Utiliza una actividad `unreliableActivity` que no tiene una anotación `@ExponentialRetry`. La interfaz de flujo de trabajo se implementa en `RetryWorkflow` y tiene un método, `process`, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en `DecoratorRetryWorkflowImpl`, de la siguiente manera: 

```
public class DecoratorRetryWorkflowImpl implements RetryWorkflow {
   ...
  public void process() {
      long initialRetryIntervalSeconds = 5;
      int maximumAttempts = 5;
      ExponentialRetryPolicy retryPolicy = new ExponentialRetryPolicy(
              initialRetryIntervalSeconds).withMaximumAttempts(maximumAttempts);

      Decorator retryDecorator = new RetryDecorator(retryPolicy);
      client = retryDecorator.decorate(RetryActivitiesClient.class, client);
      handleUnreliableActivity();
  }

  public void handleUnreliableActivity() {
      client.unreliableActivity();
  }
}
```

El flujo de trabajo funciona de la siguiente manera: 

1. `process` crea y configura un objeto `ExponentialRetryPolicy` de la siguiente manera: 
   + Pasando el intervalo de reintentos inicial al constructor.
   + Llamando al método `withMaximumAttempts` del objeto para establecer el número máximo de intentos en 5. `ExponentialRetryPolicy` expone otros objetos `with` que se pueden utilizar para especificar otras opciones de configuración.

1. `process` crea un objeto `RetryDecorator` denominado `retryDecorator` y pasa el objeto `ExponentialRetryPolicy` del Paso 1 al constructor.

1. `process` aplica el decorador a la actividad llamando al método `retryDecorator.decorate` y pasándole el nombre de la clase del cliente de la actividad.

1. `handleUnreliableActivity` ejecuta la actividad. 

Si se produce un error en la actividad, el marco de trabajo lo reintenta en función de la configuración especificada en el Paso 1.

**nota**  
Varios de los métodos `with` de la clase `ExponentialRetryPolicy` tienen un método `set` correspondiente que puede llamar para modificar la opción de configuración correspondiente en cualquier momento: `setBackoffCoefficient`, `setMaximumAttempts`, `setMaximumRetryIntervalSeconds` y `setMaximumRetryExpirationIntervalSeconds`. 

### Reintento exponencial con la clase AsyncRetryingExecutor
<a name="features-retry-exponential-async"></a>

La clase `RetryDecorator` ofrece más flexibilidad en la configuración del proceso de reintento que `@ExponentialRetry`, pero el marco de trabajo sigue ejecutando los reintentos automáticamente, en función de la configuración actual del objeto `ExponentialRetryPolicy`. Un enfoque más flexible consiste en usar la clase `AsyncRetryingExecutor`. Además de permitirle configurar el proceso de reintento en el tiempo de ejecución, el marco de trabajo llama a un método `AsyncRunnable.run` implementado por el usuario para que ejecute cada reintento en lugar de simplemente ejecutar la actividad.

Este es el patrón básico: 

1. Cree y configure un objeto `ExponentialRetryPolicy` para especificar la configuración de reintento.

1. Cree un objeto `AsyncRetryingExecutor` y pásele el objeto `ExponentialRetryPolicy` y una instancia del reloj del flujo de trabajo.

1.  Implemente una clase `TryCatch` o `TryCatchFinally` anidada anónima.

1. Implemente una clase `AsyncRunnable` anónima y anule el método `run` para la implementación del código personalizado para la ejecución de la actividad.

1.  Anule `doTry` para llamar al método `execute` del objeto `AsyncRetryingExecutor` y pasarle la clase `AsyncRunnable` del Paso 4. El objeto `AsyncRetryingExecutor` llama a `AsyncRunnable.run` para ejecutar la actividad.

1. Si se produce un error en la actividad, el objeto `AsyncRetryingExecutor` llama de nuevo al método `AsyncRunnable.run` en función de la política de reintentos especificada en el Paso 1. 

El siguiente flujo de trabajo muestra cómo usar la clase `AsyncRetryingExecutor` para implementar una estrategia de reintento exponencial. Utiliza la misma actividad `unreliableActivity` que el flujo de trabajo `DecoratorRetryWorkflow` sobre el que hemos hablado antes. La interfaz de flujo de trabajo se implementa en `RetryWorkflow` y tiene un método, `process`, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en `AsyncExecutorRetryWorkflowImpl`, de la siguiente manera: 

```
public class AsyncExecutorRetryWorkflowImpl implements RetryWorkflow {
  private final RetryActivitiesClient client = new RetryActivitiesClientImpl();
  private final DecisionContextProvider contextProvider = new DecisionContextProviderImpl();
  private final WorkflowClock clock = contextProvider.getDecisionContext().getWorkflowClock();

  public void process() {
      long initialRetryIntervalSeconds = 5;
      int maximumAttempts = 5;
      handleUnreliableActivity(initialRetryIntervalSeconds, maximumAttempts);
  }
  public void handleUnreliableActivity(long initialRetryIntervalSeconds, int maximumAttempts) {

      ExponentialRetryPolicy retryPolicy = new ExponentialRetryPolicy(initialRetryIntervalSeconds).withMaximumAttempts(maximumAttempts);
      final AsyncExecutor executor = new AsyncRetryingExecutor(retryPolicy, clock);

      new TryCatch() {
          @Override
          protected void doTry() throws Throwable {
              executor.execute(new AsyncRunnable() {
                  @Override
                  public void run() throws Throwable {
                      client.unreliableActivity();
                  }
              });
          }
          @Override
          protected void doCatch(Throwable e) throws Throwable {
          }
      };
  }
}
```

El flujo de trabajo funciona de la siguiente manera: 

1. `process` llama al método `handleUnreliableActivity` y le pasa los ajustes de la configuración.

1. `handleUnreliableActivity` utiliza los ajustes de la configuración del Paso 1 para crear un objeto `ExponentialRetryPolicy`, `retryPolicy`.

1. `handleUnreliableActivity` crea un objeto `AsyncRetryExecutor`, `executor`, y pasa el objeto `ExponentialRetryPolicy` del Paso 2 y una instancia del reloj del flujo de trabajo al constructor.

1.  `handleUnreliableActivity` implementa una clase `TryCatch` anidada de manera anónima y anula los métodos `doTry` y `doCatch` para ejecutar los reintentos y gestionar cualquier excepción.

1. `doTry` crea una clase `AsyncRunnable` anónima y anula el método `run` para la implementación del código personalizado para la ejecución de `unreliableActivity`. Para simplificar, `run` simplemente ejecuta la actividad, pero puede implementar un enfoque más sofisticado según considere apropiado.

1. `doTry` llama a `executor.execute` y le pasa el objeto `AsyncRunnable`. `execute` llama al método `run` del objeto `AsyncRunnable` para ejecutar la actividad.

1. Si se produce un error en la actividad, el ejecutor llama a `run` de nuevo, en función de la configuración del objeto `retryPolicy`. 

Para obtener más información sobre cómo utilizar la clase `TryCatch` para gestionar errores, consulte [AWS Flow Framework para excepciones de Java](errorhandling.exceptions.md). 

## Estrategia de reintento personalizada
<a name="custom-retry-strategy"></a>

El enfoque más flexible para reintentar las actividades fallidas es una estrategia personalizada, que invoca de forma recursiva a un método asíncrono que ejecuta el reintento, al igual que la estrategia. retry-until-success No obstante, en lugar de simplemente ejecutar la actividad de nuevo, usted implementa la lógica personalizada que decide si se ejecuta cada reintento sucesivo y cómo hacerlo. Este es el patrón básico: 

1. Cree un objeto de estado `Settable<T>` que se utiliza para indicar si se ha producido un error en la actividad.

1. Implemente una clase `TryCatch` o `TryCatchFinally` anidada.

1. `doTry` ejecuta la actividad.

1. Si se produce un error en la actividad, `doCatch` establece el objeto de estado para indicar que se ha producido un error en la actividad.

1. Llame al método asíncrono de gestión de errores y pásele el objeto de estado. El método aplaza la ejecución hasta que `TryCatch` o `TryCatchFinally` se completan.

1. El método de gestión de errores decide si se vuelve a intentar la actividad y, si la respuesta es afirmativa, cuándo hacerlo.

El siguiente flujo de trabajo muestra cómo implementar una estrategia de reintento personalizada. Utiliza la misma actividad `unreliableActivity` que los flujos de trabajo `DecoratorRetryWorkflow` y `AsyncExecutorRetryWorkflow`. La interfaz de flujo de trabajo se implementa en `RetryWorkflow` y tiene un método, `process`, que es el punto de entrada del flujo de trabajo. El proceso de trabajo de flujo de trabajo se implementa en `CustomLogicRetryWorkflowImpl`, de la siguiente manera: 

```
public class CustomLogicRetryWorkflowImpl implements RetryWorkflow {
  ...
  public void process() {
      callActivityWithRetry();
  }
  @Asynchronous
  public void callActivityWithRetry() {
      final Settable<Throwable> failure = new Settable<Throwable>();
      new TryCatchFinally() {
          protected void doTry() throws Throwable {
              client.unreliableActivity();
          }
          protected void doCatch(Throwable e) {
              failure.set(e);
          }
          protected void doFinally() throws Throwable {
              if (!failure.isReady()) {
                  failure.set(null);
              }
          }
      };
      retryOnFailure(failure);
  }
  @Asynchronous
  private void retryOnFailure(Promise<Throwable> failureP) {
      Throwable failure = failureP.get();
      if (failure != null && shouldRetry(failure)) {
          callActivityWithRetry();
      }
  }
  protected Boolean shouldRetry(Throwable e) {
      //custom logic to decide to retry the activity or not
      return true;
  }
}
```

El flujo de trabajo funciona de la siguiente manera:

1. `process` llama al método asíncrono `callActivityWithRetry`.

1. `callActivityWithRetry` crea un objeto `Settable<Throwable>` llamado failure que se utiliza para indicar si se ha producido un error en la actividad. `Settable<T>` proviene de `Promise<T>` y funciona de forma muy parecida, pero en este caso usted establece manualmente el valor de un objeto `Settable<T>`.

1. `callActivityWithRetry` implementa una clase `TryCatchFinally` anidada de manera anónima para gestionar cualquier excepción lanzada por `unreliableActivity`. Para obtener más información sobre cómo tratar excepciones lanzadas por un código asíncrono, consulte [AWS Flow Framework para excepciones de Java](errorhandling.exceptions.md).

1. `doTry` ejecuta `unreliableActivity`.

1. Si `unreliableActivity` lanza una excepción, el marco de trabajo llama a `doCatch` y le pasa el objeto de excepción. `doCatch` establece `failure` en el objeto de excepción, lo que indica que se ha producido un error en la actividad y pone el objeto en el estado ready.

1. `doFinally` comprueba si `failure` está listo, lo que solo será "true" si `doCatch` ha establecido `failure`.
   + Si está listo`failure`, no hace nada. `doFinally`
   + Si `failure` no está listo, la actividad completada y `doFinally` establecen el error en `null`. 

1. `callActivityWithRetry` llama al método asíncrono `retryOnFailure` y le pasa el error. Dado que el error es un tipo `Settable<T>`, `callActivityWithRetry` la ejecución se aplaza hasta que el error esté listo, lo que ocurre después de que se completa `TryCatchFinally`.

1. `retryOnFailure` obtiene el valor del error.
   + Si el error se establece en null, el reintento se ha realizado con éxito. `retryOnFailure` no hace nada, lo cual termina el proceso de reintento.
   + Si el error se establece en un objeto de excepción y `shouldRetry` devuelve "true", `retryOnFailure` llama a `callActivityWithRetry` para reintentar la actividad. 

     `shouldRetry` implementa la lógica personalizada para decidir si vuelve a intentar una actividad en la que se ha producido un error. Para simplificar, `shouldRetry` siempre devuelve `true` y `retryOnFailure` ejecuta la actividad inmediatamente, pero puede implementar una lógica más sofisticada según considere apropiado. 

1. Los pasos 2 al 8 se repiten hasta que `unreliableActivity` se completa, o bien hasta que `shouldRetry` decide interrumpir el proceso. 

**nota**  
`doCatch` no gestiona el proceso de reintento, simplemente establece el error para indicar que se ha producido un error en la actividad. El proceso de reintento es gestionado por el método asíncrono `retryOnFailure`, que aplaza la ejecución hasta que se completa `TryCatch`. El motivo de este enfoque es que, si reintenta una actividad en `doCatch`, no es posible cancelarla. Volver a intentar la actividad `retryOnFailure` le permite ejecutar actividades que se pueden cancelar. 