

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Relance des activités ayant échoué
<a name="features-retry"></a>

Les activités échouent parfois pour des raisons éphémères comme une perte temporaire de connectivité. L'activité peut réussir à un autre moment. La méthode appropriée pour résoudre des échecs d'activité consiste donc souvent à relancer l'activité, peut-être plusieurs fois. 

Il existe différentes stratégies pour relancer des activités ; celle qui convient le mieux dépend des détails de votre flux de travail. Les stratégies se répartissent en trois catégories de base : 
+ La retry-until-success stratégie continue simplement de réessayer l'activité jusqu'à ce qu'elle soit terminée.
+ La stratégie de nouvelle tentative exponentielle augmente de façon exponentielle l'intervalle de temps entre les tentatives jusqu'à ce que l'activité se termine ou que le processus atteigne un point d'arrêt spécifié, comme un nombre maximal de tentatives.
+ La stratégie de nouvelle tentative personnalisée décide s'il faut relancer l'activité après chaque tentative ayant échoué et de quelle manière.

Les sections suivantes expliquent comment implémenter ces stratégies. Les exemples de exécuteurs de flux de travail utilisent tous une activité unique, `unreliableActivity`, qui exécute de façon aléatoire les actions suivantes : 
+ Elle se termine immédiatement
+ Elle échoue intentionnellement en dépassant la valeur de délai d'expiration
+ Elle échoue intentionnellement en déclenchant l'exception `IllegalStateException` 

## Retry-Until-Success Stratégie
<a name="features-retry-success"></a>

La stratégie de nouvelle tentative la plus simple consiste à relancer chaque fois l'activité jusqu'à ce que celle-ci réussisse. Le modèle de base est le suivant :

1. Implémenter une classe `TryCatch` ou `TryCatchFinally` imbriquée dans la méthode de point d'entrée de votre flux de travail.

1. Exécuter l'activité dans `doTry`.

1. Si l'activité échoue, l'infrastructure appelle `doCatch`, qui exécute à nouveau la méthode de point d'entrée.

1. Répéter les étapes 2 à 3 jusqu'à ce que l'activité se termine correctement.

Le flux de travail suivant met en œuvre la retry-until-success stratégie. L'interface de flux de travail est implémentée dans `RetryActivityRecipeWorkflow` et comporte une méthode, `runUnreliableActivityTillSuccess`, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans `RetryActivityRecipeWorkflowImpl`, comme suit : 

```
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();
        }
    }
}
```

Le flux de travail fonctionne comme suit : 

1. `runUnreliableActivityTillSuccess` crée un objet `Settable<Boolean>` nommé `retryActivity` qui est utilisé pour indiquer si l'activité a échoué et doit être réessayée. `Settable<T>` est dérivé de `Promise<T>` et fonctionne de la même manière, mais vous définissez une valeur de l'objet `Settable<T>` manuellement.

1. `runUnreliableActivityTillSuccess` implémente une classe `TryCatch` imbriquée anonyme pour traiter les exceptions qui sont déclenchées par l'activité `unreliableActivity`. Pour en savoir plus sur le traitement des exceptions déclenchées par un code asynchrone, consultez [Gestion des erreurs](errorhandling.md).

1. `doTry` exécute l'activité `unreliableActivity` qui renvoie un objet `Promise<Void>` nommé `activityRanSuccessfully`.

1. `doTry` appelle la méthode `setRetryActivityToFalse` asynchrone et lui transmet deux paramètres :
   + `activityRanSuccessfully` prend l'objet `Promise<Void>` renvoyé par l'activité `unreliableActivity`.
   + `retryActivity` prend l'objet `retryActivity`.

   Si `unreliableActivity` se termine, `activityRanSuccessfully` devient prêt et `setRetryActivityToFalse` définit `retryActivity` sur false. Sinon, `activityRanSuccessfully` ne devient jamais prêt et `setRetryActivityToFalse` ne s'exécute pas.

1. Si `unreliableActivity` déclenche une exception, l'infrastructure appelle `doCatch` et lui transmet l'objet d'exception. `doCatch` définit `retryActivity` avec la valeur true.

1. `runUnreliableActivityTillSuccess` appelle la méthode `restartRunUnreliableActivityTillSuccess` asynchrone et lui transmet l'objet `retryActivity`. Comme `retryActivity` est de type `Promise<T>`, `restartRunUnreliableActivityTillSuccess` diffère l'exécution jusqu'à ce que `retryActivity` soit prêt, ce qui a lieu une fois que `TryCatch` est terminé. 

1. Quand `retryActivity` est prêt, `restartRunUnreliableActivityTillSuccess` extrait la valeur.
   + Si la valeur est `false`, la nouvelle tentative a réussi. `restartRunUnreliableActivityTillSuccess` ne fait rien et la séquence de nouvelle tentative est arrêtée.
   + Si la valeur est true, la nouvelle tentative a échoué. `restartRunUnreliableActivityTillSuccess` appelle `runUnreliableActivityTillSuccess` pour exécuter l'activité à nouveau.

1. Le flux de travail répète les étapes 1 à 7 jusqu'à ce que `unreliableActivity` se termine. 

**Note**  
`doCatch` ne traite pas l'exception ; il définit simplement l'objet `retryActivity` sur true pour indiquer que l'activité a échoué. La nouvelle tentative est traitée par la méthode `restartRunUnreliableActivityTillSuccess` asynchrone, ce qui diffère l'exécution jusqu'à ce que `TryCatch` se termine. La raison de cette approche est que si vous relancez une activité dans `doCatch`, vous ne pouvez pas l'annuler. La relance de l'activité dans `restartRunUnreliableActivityTillSuccess` vous permet d'exécuter des activités annulables. 

## Stratégie de nouvelle tentative exponentielle
<a name="features-retry-exponential"></a>

Avec la stratégie de nouvelle tentative exponentielle, l'infrastructure exécute à nouveau une activité ayant échoué après une période de temps spécifiée, N secondes. Si cette tentative échoue, l'infrastructure exécute à nouveau l'activité après 2N secondes, puis après 4N secondes, et ainsi de suite. Comme le temps d'attente peut devenir très long, vous arrêtez généralement les nouvelles tentatives après un certain temps plutôt que de continuer indéfiniment.

L'infrastructure fournit trois façons d'implémenter une stratégie de nouvelle tentative exponentielle :
+ L'annotation `@ExponentialRetry` est l'approche la plus simple, mais vous devez définir les options de configuration de nouvelle tentative lors de la compilation.
+ La classe `RetryDecorator` vous permet de définir la configuration de nouvelle tentative lors de l'exécution et de la modifier si nécessaire.
+ La classe `AsyncRetryingExecutor` vous permet de définir la configuration de nouvelle tentative lors de l'exécution et de la modifier si nécessaire. En outre, l'infrastructure appelle une méthode `AsyncRunnable.run` implémentée par l'utilisateur pour exécuter chaque nouvelle tentative.

Toutes les approches prennent en charge les options de configuration suivantes, où les valeurs de temps sont exprimées en secondes : 
+ Le temps d'attente initial avant une nouvelle tentative.
+ Le coefficient de recul qui est utilisé pour calculer les intervalles de nouvelle tentative, comme suit :

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

  La valeur par défaut est 2.0.
+ Le nombre maximum de nouvelles tentatives autorisées. La valeur par défaut est unlimited (illimité).
+ L'intervalle maximum de nouvelle tentative. La valeur par défaut est unlimited (illimité).
+ Le délai d'expiration. Les nouvelles tentatives s'arrêtent lorsque la durée totale du processus dépasse cette valeur. La valeur par défaut est unlimited (illimité).
+ Les exceptions qui déclenchent le processus de nouvelle tentative. Par défaut, toutes les exceptions déclenchent le processus de nouvelle tentative.
+ Les exceptions qui ne déclenchent pas le processus de nouvelle tentative. Par défaut, aucune exception n'est exclue.

Les sections suivantes décrivent les différentes façons d'implémenter une stratégie de nouvelle tentative exponentielle.

### Réessayer de façon exponentielle avec @ ExponentialRetry
<a name="features-retry-exponential-annotation"></a>

La façon la plus simple d'implémenter une stratégie de nouvelle tentative exponentielle pour une activité est d'appliquer une annotation `@ExponentialRetry ` à l'activité dans la définition d'interface. Si l'activité échoue, l'infrastructure gère automatiquement le processus de nouvelle tentative en fonction des valeurs d'option spécifiées. Le modèle de base est le suivant :

1. Appliquer `@ExponentialRetry` aux activités appropriées et spécifier la configuration de nouvelle tentative.

1. Si une activité annotée échoue, l'infrastructure la relance automatiquement en fonction de la configuration spécifiée par les arguments de l'annotation. 

L'exécuteur de flux de travail `ExponentialRetryAnnotationWorkflow` implémente la stratégie de nouvelle tentative exponentielle en utilisant une annotation `@ExponentialRetry`. Il utilise une activité `unreliableActivity` dont la définition d'interface est implémentée dans `ExponentialRetryAnnotationActivities` comme suit :

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

Les options `@ExponentialRetry` spécifient la stratégie suivante : 
+ Effectuer une nouvelle tentative uniquement si l'activité déclenche `IllegalStateException`.
+ Utiliser un temps d'attente initial de 5 secondes.
+ Pas plus de 5 nouvelles tentatives. 

L'interface de flux de travail est implémentée dans `RetryWorkflow` et comporte une méthode, `process`, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans `ExponentialRetryAnnotationWorkflowImpl`, comme suit : 

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

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

Le flux de travail fonctionne comme suit : 

1. `process` exécute la méthode `handleUnreliableActivity` synchrone.

1. `handleUnreliableActivity` exécute l'activité `unreliableActivity`. 

Si l'activité échoue en déclenchant `IllegalStateException`, l'infrastructure exécute automatiquement la stratégie de nouvelle tentative spécifiée dans `ExponentialRetryAnnotationActivities`.

### Réessai exponentiel avec la classe RetryDecorator
<a name="features-retry-exponential-decorator"></a>

`@ExponentialRetry` est simple à utiliser. Par contre, la configuration est statique et définie lors de la compilation. L'infrastructure utilise donc la même stratégie de nouvelle tentative chaque fois que l'activité échoue. Vous pouvez implémenter une stratégie de nouvelle tentative exponentielle plus flexible à l'aide de la classe `RetryDecorator`, qui vous permet de spécifier la configuration pendant l'exécution et de la modifier si nécessaire. Le modèle de base est le suivant :

1. Créer et configurer un objet `ExponentialRetryPolicy` qui spécifie la configuration de nouvelle tentative.

1. Créer un objet `RetryDecorator` et transmettre l'objet `ExponentialRetryPolicy` de l'étape 1 au constructeur.

1. Appliquer l'objet décorateur à l'activité en transmettant le nom de classe du client d'activité à la méthode de décoration de l'objet `RetryDecorator`.

1. Exécuter l'activité.

Si l'activité échoue, l'infrastructure la relance automatiquement en fonction de la configuration de l'objet `ExponentialRetryPolicy`. Vous pouvez modifier la configuration de nouvelle tentative si nécessaire en modifiant cet objet. 

**Note**  
L'annotation `@ExponentialRetry` et la classe `RetryDecorator` s'excluent mutuellement. Vous ne pouvez pas utiliser `RetryDecorator` pour remplacer dynamiquement une stratégie de nouvelle tentative spécifiée par une annotation `@ExponentialRetry`. 

L'implémentation de flux de travail suivante montre comment utiliser la classe `RetryDecorator` pour implémenter une stratégie de nouvelle tentative exponentielle. Elle utilise une activité `unreliableActivity` qui ne comporte pas d'annotation `@ExponentialRetry`. L'interface de flux de travail est implémentée dans `RetryWorkflow` et comporte une méthode, `process`, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans `DecoratorRetryWorkflowImpl`, comme suit : 

```
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();
  }
}
```

Le flux de travail fonctionne comme suit : 

1. `process` crée et configure un objet `ExponentialRetryPolicy` en : 
   + Transmettant l'intervalle de nouvelle tentative initial au constructeur.
   + Appel de la méthode `withMaximumAttempts` de l'objet pour définir le nombre maximal de tentatives sur 5. `ExponentialRetryPolicy` expose d'autres objets `with` que vous pouvez utiliser pour spécifier d'autres options de configuration.

1. `process` crée un objet `RetryDecorator` nommé `retryDecorator` et transmet l'objet `ExponentialRetryPolicy` de l'étape 1 au constructeur.

1. `process` applique l'objet décorateur à l'activité en appelant la méthode `retryDecorator.decorate` et en lui transmettant le nom de classe du client d'activité.

1. `handleUnreliableActivity` exécute l'activité. 

Si une activité échoue, l'infrastructure la relance en fonction de la configuration spécifiée à l'étape 1.

**Note**  
Plusieurs des méthodes `with` de la classe `ExponentialRetryPolicy` ont une méthode `set` correspondante que vous pouvez appeler pour modifier l'option de configuration correspondante à tout moment : `setBackoffCoefficient`, `setMaximumAttempts`, `setMaximumRetryIntervalSeconds` et `setMaximumRetryExpirationIntervalSeconds`. 

### Réessai exponentiel avec la classe AsyncRetryingExecutor
<a name="features-retry-exponential-async"></a>

La classe `RetryDecorator` offre plus de flexibilité pour la configuration du processus de nouvelle tentative que `@ExponentialRetry`, mais l'infrastructure exécute toujours les nouvelles tentatives automatiquement, en fonction de la configuration actuelle de l'objet `ExponentialRetryPolicy`. Une approche plus souple consiste à utiliser la classe `AsyncRetryingExecutor`. En plus de vous permettre de configurer le processus de nouvelle tentative pendant l'exécution, l'infrastructure appelle une méthode `AsyncRunnable.run` implémentée par l'utilisateur pour exécuter chaque nouvelle tentative au lieu de simplement exécuter l'activité.

Le modèle de base est le suivant : 

1. Créer et configurer un objet `ExponentialRetryPolicy` pour spécifier la configuration de nouvelle tentative.

1. Créer un objet `AsyncRetryingExecutor`, et lui transmettre l'objet `ExponentialRetryPolicy` et une instance de l'horloge de flux de travail.

1.  Implémenter une classe `TryCatch` ou `TryCatchFinally` imbriquée anonyme.

1. Implémenter une classe `AsyncRunnable` anonyme et remplacer la méthode `run` pour implémenter un code personnalisé afin d'exécuter l'activité.

1.  Remplacer `doTry` pour appeler la méthode `execute` de l'objet `AsyncRetryingExecutor` et lui transmettre la classe `AsyncRunnable` de l'étape 4. L'objet `AsyncRetryingExecutor` appelle `AsyncRunnable.run` pour exécuter l'activité.

1. Si l'activité échoue, l'objet `AsyncRetryingExecutor` appelle à nouveau la méthode `AsyncRunnable.run`, en fonction de la stratégie de nouvelle tentative spécifiée à l'étape 1. 

Le flux de travail suivant montre comment utiliser la classe `AsyncRetryingExecutor` pour implémenter une stratégie de nouvelle tentative exponentielle. Il utilise la même activité `unreliableActivity` que le flux de travail `DecoratorRetryWorkflow` présenté précédemment. L'interface de flux de travail est implémentée dans `RetryWorkflow` et comporte une méthode, `process`, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans `AsyncExecutorRetryWorkflowImpl`, comme suit : 

```
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 {
          }
      };
  }
}
```

Le flux de travail fonctionne comme suit : 

1. `process` appelle la méthode `handleUnreliableActivity` et lui transmet les paramètres de configuration.

1. `handleUnreliableActivity` utilise les paramètres de configuration de l'étape 1 pour créer un objet `ExponentialRetryPolicy`, `retryPolicy`.

1. `handleUnreliableActivity` crée un objet `AsyncRetryExecutor`, `executor`, et transmet l'objet `ExponentialRetryPolicy` de l'étape 2 et une instance de l'horloge de flux de travail au constructeur.

1.  `handleUnreliableActivity` implémente une classe `TryCatch` imbriquée anonyme, et remplace les méthodes `doTry` et `doCatch` pour exécuter les nouvelles tentatives et traiter les exceptions.

1. `doTry` crée une classe `AsyncRunnable` anonyme et remplace la méthode `run` pour implémenter un code personnalisé afin d'exécuter `unreliableActivity`. Pour des raisons de simplicité, `run` exécute seulement l'activité, mais vous pouvez implémenter des approches plus sophistiquées le cas échéant.

1. `doTry` appelle `executor.execute` et transmet l'objet `AsyncRunnable`. `execute` appelle la méthode `run` de l'objet `AsyncRunnable` pour exécuter l'activité.

1. Si l'activité échoue, l'exécuteur appelle à nouveau `run` en fonction de la configuration de l'objet `retryPolicy`. 

Pour en savoir plus sur l'utilisation de la classe `TryCatch` pour gérer des erreurs, consultez [AWS Flow Framework pour les exceptions Java](errorhandling.exceptions.md). 

## Stratégie de nouvelle tentative personnalisée
<a name="custom-retry-strategy"></a>

L'approche la plus flexible pour réessayer les activités ayant échoué est une stratégie personnalisée, qui appelle de manière récursive une méthode asynchrone qui exécute la nouvelle tentative, un peu comme la stratégie. retry-until-success Par contre, au lieu de relancer simplement l'activité, vous implémentez une logique personnalisée qui décide si chaque nouvelle tentative successive doit être exécutée et de quelle façon. Le modèle de base est le suivant : 

1. Créer un objet de statut `Settable<T>` qui est utilisé pour indiquer si l'activité a échoué.

1. Implémenter une classe `TryCatch` ou `TryCatchFinally` imbriquée.

1. `doTry` exécute l'activité.

1. Si l'activité échoue, `doCatch` définit l'objet de statut pour indiquer que l'activité a échoué.

1. Appeler une méthode de gestion des défaillances et lui transmettre l'objet de statut. La méthode diffère l'exécution jusqu'à ce que `TryCatch` ou `TryCatchFinally` soit terminé.

1. La méthode de gestion des défaillances décide s'il faut relancer l'activité, et si oui, quand.

Le flux de travail suivant montre comment implémenter une stratégie de nouvelle tentative personnalisée. Il utilise la même activité `unreliableActivity` que les flux de travail `DecoratorRetryWorkflow` et `AsyncExecutorRetryWorkflow`. L'interface de flux de travail est implémentée dans `RetryWorkflow` et comporte une méthode, `process`, qui est le point d'entrée du flux de travail. L'exécuteur de flux de travail est implémenté dans `CustomLogicRetryWorkflowImpl`, comme suit : 

```
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;
  }
}
```

Le flux de travail fonctionne comme suit :

1. `process` appelle la méthode `callActivityWithRetry` asynchrone.

1. `callActivityWithRetry` crée un objet `Settable<Throwable>` nommé failure qui est utilisé pour indiquer si l'activité a échoué. `Settable<T>` est dérivé de `Promise<T>` et fonctionne de la même manière, mais vous définissez une valeur de l'objet `Settable<T>` manuellement.

1. `callActivityWithRetry` implémente une classe `TryCatchFinally` imbriquée anonyme pour traiter les exceptions qui sont déclenchées par `unreliableActivity`. Pour en savoir plus sur le traitement des exceptions déclenchées par un code asynchrone, consultez [AWS Flow Framework pour les exceptions Java](errorhandling.exceptions.md).

1. `doTry` exécute `unreliableActivity`.

1. Si `unreliableActivity` lève une exception, le framework appelle `doCatch` et transmet l'objet d'exception. `doCatch` définit `failure` sur l'objet d'exception, ce qui indique que l'activité a échoué et place l'objet dans l'état prêt.

1. `doFinally` vérifie si `failure` est prêt, ce qui est vrai seulement si `failure` a été défini par `doCatch`.
   + S'il `failure` est prêt, il `doFinally` ne fait rien.
   + Si `failure` n'est pas prêt, l'activité est terminée et `doFinally` définit la défaillance (failure) sur `null`. 

1. `callActivityWithRetry` appelle la méthode `retryOnFailure` asynchrone et lui transmet « failure ». Comme « failure » est de type `Settable<T>`, `callActivityWithRetry` diffère l'exécution jusqu'à ce que « failure » soit prêt, ce qui a lieu une fois que `TryCatchFinally` est terminé.

1. `retryOnFailure` extrait la valeur de « failure ».
   + Si l'objet failure est défini avec la valeur null, la nouvelle tentative est réussie. `retryOnFailure` ne fait rien, ce qui arrête le processus de nouvelle tentative.
   + Si « failure » est défini sur un objet d'exception et que `shouldRetry` renvoie true, `retryOnFailure` appelle `callActivityWithRetry` pour relancer l'activité. 

     `shouldRetry` implémente une logique personnalisée qui décide s'il faut relancer une activité ayant échoué. Pour des raisons de simplicité, `shouldRetry` renvoie toujours `true` et `retryOnFailure` exécute immédiatement l'activité, mais vous pouvez implémenter une logique plus sophistiquée le cas échéant. 

1. Les étapes 2 à 8 se répètent jusqu'à ce que `unreliableActivity` le processus soit terminé ou qu'il soit `shouldRetry` décidé d'arrêter le processus. 

**Note**  
`doCatch` ne traite pas le processus de nouvelle tentative ; il définit simplement « failure » pour indiquer que l'activité a échoué. Le processus de nouvelle tentative est géré par la méthode `retryOnFailure` asynchrone, qui diffère l'exécution jusqu'à ce que `TryCatch` se termine. La raison de cette approche est que si vous relancez une activité dans `doCatch`, vous ne pouvez pas l'annuler. La relance de l'activité dans `retryOnFailure` vous permet d'exécuter des activités annulables. 