HOAB

History of a bug

@EJB @Asynchronous ExceptionExecution

Rédigé par gorki Aucun commentaire

Le problème :

La récupération des résultats des EJB asynchrone.

La technique de base pour déclencher un traitement asynchrone :

  1. créer une méthode taggué "@Asynchronous", la méthode retourne un Future<?>
  2. l'appeler
  3. attendre le retour

Souvent, on déclenche X fois la méthode, le tout passe dans une boucle :

  1. Pour toutes les tâches
    1. créer une méthode taggué "@Asynchronous", la méthode retourne un Future<?>
    2. l'appeler
    3. stocker le Future
  2. attendre que tous les Futures soit terminés
  3. récupérer les résultats

On part du principe que la méthode asynchone gère correctement ses erreurs à son niveau (un try/catch Exception avec un retour Future correct).

Seulement, certaines exceptions peuvent être remontées par le container : transaction timeout, erreur lors du commit, etc... sous forme d'ExecutionException. Et là, si le code est mal fait, on peut avoir des surprises et ne pas savoir dire ce qui s'est passé exactement

Solution :

Il faut donc penser lors de la récupération des résultats à :

  1. prévoir un try/catch d'ExecutionException / CancellationException / InterruptedException pour les Future.get()
  2. bien le mettre PAR Future, puisque chaque Future.get() va pouvoir lancer sa propre exception. (Certaines tâches peuvent provoquer une erreur au commit et pas d'autre)
  3. gérer un état global de récupération du résultat (est-ce que le code est correct si seulement une partie des tâches ont réussi ?)
  4. la gestion de traces et d'erreurs associés pour permettre de retrouver les tâches en erreur.

Exemple :

  • ici, le résultat est retourné dans une map
  • sinon le bean lui-même est retourné avec l'exception, il contient des maps pour stocker les erreurs et les futures
  • la gestion de traces et d'erreur est faite par l'appelant, les informations nécessaires pour décrire le contexte métier du Future sont dans un bean "FutureDescription"
// Attributs : 
private Map<Future<R>, FutureDescription> futures = new ConcurrentHashMap<Future<R>, FutureDescription>();
private Map<Future<R>, Exception> futureException = new ConcurrentHashMap<Future<R>, Exception>();

...

public Map<Future<R>, R> extractResultsMap(boolean throwException) throws MultiThreadException {

Map<Future<R>, R> results = new HashMap<Future<R>, R>();

// parcours de chaque Future pour récupérer son résultat d'exécution
for (Future<R> future : futures.keySet()) {
   try {
         results.put(future, future.get());
   } catch (InterruptedException | ExecutionException e) {
         futureException.put(future, e);
   }
}
if (throwException && (futureException.size() > 0)) {
    throw new MultiThreadException(ErrorCode.TECHNICAL_GLOBAL, "Erreur pendant la recuperation des resultats", this);
}
return results;

}

 

 

Couplés avec :

  • une classe qui fait du monitor pour tracer l'avancement des threads
  • une classe qui stocke les Future, leur résultats, leurs exceptions
  • des méthodes utilitaires

Cela donne les outils nécessaire pour gérer facilement l'asynchronisme.

Fil RSS des articles de ce mot clé