Aller au contenu principal

Durabilité et fiabilité des Workflows

Mis à jour le 29 juillet 2026

Pourquoi la durabilité change tout

Imaginez un pipeline de dix étapes qui traite un lot de factures : extraction, contrôle, appel modèle, écriture en base, notification. Dans un script Python classique, un plantage à l’étape 7 vous ramène à l’étape 1, et les six premières s’exécutent une seconde fois — avec toutes les conséquences que cela suppose si l’une d’elles écrivait en base ou envoyait un message. Avec un workflow durable, le système reprend exactement à l’étape 7 : sans refaire les six premières, sans dupliquer d’actions, sans perdre de données.

Cette propriété repose sur un mécanisme précis appelé event sourcing, qu’il vaut la peine de comprendre car il explique aussi les contraintes imposées par la suite.

Event sourcing : le principe

L’event sourcing consiste à enregistrer chaque action du workflow comme un événement immuable dans un journal (log). La différence avec une approche classique tient en une phrase : au lieu de stocker uniquement l’état actuel — « en cours, étape 7 » — la plateforme conserve l’historique complet des opérations, avec leurs résultats. L’exécution d’un workflow à deux activités laisse ainsi la trace suivante :

  1. Le workflow démarre et reçoit ses paramètres d’entrée → événement enregistré
  2. Le workflow appelle l’activité A → événement enregistré
  3. L’activité A termine avec un résultat → événement enregistré
  4. Le workflow appelle l’activité B → événement enregistré
  5. L’activité B termine → événement enregistré
  6. Le workflow retourne son résultat final → événement enregistré

Chaque étape est persistée avant de passer à la suivante : aucune fenêtre ne permet à une action d’avoir eu lieu sans que le journal ne le sache. Ce journal constitue dès lors la source de vérité absolue, l’état du workflow n’existant nulle part ailleurs.

Replay automatique : la reprise sur panne

Lorsqu’un worker tombe en panne, un nouveau worker prend le relais, récupère le journal d’événements et rejoue (replay) le code du workflow depuis le début. Ce mot « rejoue » surprend souvent : le code repart bien de sa première ligne, mais pour chaque activité déjà terminée, le moteur ne rappelle pas votre fonction — il substitue le résultat enregistré dans le journal. Le workflow avance donc à vitesse de lecture jusqu’à la première activité non terminée, et c’est seulement là que du travail réel reprend.

Les activités déjà réussies ne sont ainsi jamais réexécutées lors d’un replay. L’exemple ci-dessous rend la garantie tangible sur le cas le plus sensible qui soit, l’envoi d’un email.

import mistralai.workflows as workflows

@workflows.activity()
async def envoyer_email(destinataire: str, contenu: str) -> dict:
    """Cette activité ne sera exécutée qu'une seule fois,
    même si le workflow est rejoué plusieurs fois."""
    response = await service_email.envoyer(destinataire, contenu)
    return {"status": "envoyé", "id": response.id}

@workflows.workflow.define(name="notification_workflow")
class NotificationWorkflow:
    @workflows.workflow.entrypoint
    async def run(self, destinataire: str) -> dict:
        # Si le workflow plante APRÈS l'envoi de l'email,
        # l'email ne sera PAS renvoyé lors du replay.
        resultat_email = await envoyer_email(destinataire, "Votre rapport est prêt")

        # Le workflow reprendra ici
        resultat_log = await enregistrer_notification(destinataire, resultat_email)
        return resultat_log

Tolérance aux pannes : les scénarios couverts

Quatre familles d’incidents sont traitées sans que vous ayez une ligne à écrire. Le crash du worker d’abord : le processus Python s’arrête brutalement, qu’il s’agisse d’une erreur mémoire, d’un kill ou d’un redémarrage serveur, et un autre worker reprend le workflow là où il en était. L’erreur transitoire réseau ensuite : un appel API échoue sur un timeout, et la politique de retry relance automatiquement l’activité avec un backoff exponentiel, c’est-à-dire des délais croissants qui évitent de marteler un service déjà en difficulté.

Viennent enfin les erreurs de votre propre code — une exception levée dans une activité, relancée un nombre défini de fois selon la politique configurée avant que l’échec ne soit définitif — et l’indisponibilité temporaire d’un service tiers. Sur ce dernier point, l’effet est spectaculaire : une API partenaire down dix minutes ne fait plus échouer votre pipeline nocturne, les activités en attente sont relancées quand le service revient et le traitement se termine avec dix minutes de retard.

La contrainte de déterminisme

Le replay a un prix. Pour que la relecture du journal reproduise fidèlement le déroulé initial, le code du workflow — pas des activités — doit être déterministe : à entrées identiques, même séquence d’opérations. Si votre workflow branchait hier sur if datetime.now().hour < 12, le replay de cette nuit peut prendre l’autre branche et désaligner complètement le journal.

Dans le code d’un workflow, vous ne devez donc jamais :

  • Utiliser datetime.now() → utilisez workflow.now()
  • Utiliser uuid.uuid4() → utilisez workflow.uuid4()
  • Utiliser random.random() → utilisez workflow.random()
  • Faire des appels réseau ou I/O directement → déplacez-les dans une activité

Ces trois substitutions renvoient des valeurs enregistrées au premier passage et rejouées à l’identique ensuite. Cette contrainte sera détaillée dans la leçon 8 consacrée au déterminisme.

Comparaison avec les approches classiques

ApprocheReprise sur panneGarantie d’exécutionComplexité
Script Python simpleNonAucuneFaible
File de messages (Celery, RQ)PartielleAt-least-onceMoyenne
Cron + base de donnéesManuelleDépend de l’implémentationÉlevée
Workflows Mistral (Temporal)AutomatiqueExactly-once (logique)Faible (SDK)

Lisez surtout la colonne complexité : une file de messages paraît simple à démarrer, mais sa reprise partielle vous oblige à écrire vous-même la gestion d’état que le SDK fournit ici d’emblée.

Exactly-once : une garantie logique

La plateforme garantit que la logique de votre workflow s’exécute exactement une fois. Si une activité réussit, son résultat est enregistré et ne sera jamais réexécuté ; si elle échoue, elle est retentée selon la politique configurée.

Le mot « logique » n’est pas un ornement. Au niveau réseau, un appel peut parfaitement être envoyé deux fois : votre requête de création de client arrive bien chez le CRM, mais l’accusé de réception se perd en route. De votre côté, l’activité a échoué et sera retentée, alors que le client existe déjà. D’où l’exigence d’activités idempotentes — même résultat si elles sont appelées plusieurs fois avec les mêmes paramètres — ce qui revient en pratique à préférer un PUT sur un identifiant stable à un POST qui crée une ligne de plus à chaque appel.

Points clés à retenir

  • L’event sourcing enregistre chaque étape comme un événement immuable dans un journal
  • Le replay automatique permet de reprendre un workflow interrompu sans réexécuter les activités déjà terminées
  • La plateforme gère automatiquement les crashs, erreurs réseau et indisponibilités de services
  • Le code du workflow doit être déterministe pour que le replay fonctionne
  • Les activités doivent être idempotentes pour garantir la cohérence en cas de retry
  • La garantie d’exécution est exactly-once au niveau logique