Dans le paysage des systèmes distribués modernes, écrire des tâches en arrière-plan fiables et des processus multi-étapes complexes est notoirement difficile. Les approches traditionnelles s'appuient souvent sur des files de messages, des machines à états ou de simples tâches cron, mais ces solutions peinent souvent à offrir une visibilité, un débogage et une gestion des échecs partiels. Entrez Temporal, une plateforme d'exécution de workflows qui permet aux développeurs d'écrire la logique métier comme du code standard, garantissant la durabilité et la fiabilité sans sacrifier l'expérience développeur.
La proposition de valeur centrale de Temporal est l'"Exécution Durable" (Durable Execution). Contrairement au code traditionnel qui s'exécute jusqu'à son crash ou sa complétion, Temporal enregistre l'historique de chaque appel de fonction. Si un processus échoue en cours d'exécution, Temporal peut restaurer le processus depuis son dernier point de contrôle et continuer l'exécution exactement là où il s'était arrêté. Ce changement de paradigme transfère la complexité de la gestion d'état du développeur vers la plateforme.
L'architecture de base : Workflows et Activités
Pour comprendre Temporal, vous devez distinguer ses deux composants principaux : les Workflows et les Activités.
- Workflows : La logique d'orchestration. Ils définissent l'ordre des opérations, gèrent les nouvelles tentatives et les dépassements de délai. Il est crucial que le code du Workflow soit déterministe. Vous ne pouvez pas utiliser de générateurs de nombres aléatoires, d'horodatages actuels ou d'appels directs à la base de données au sein d'une fonction Workflow. Au lieu de cela, vous interagissez avec le monde extérieur via les Activités.
- Activités : Le travail effectif ayant des effets de bord. L'envoi d'un e-mail, l'interrogation d'une base de données ou l'appel d'une API externe se font ici. Les Activités sont non déterministes et sont exécutées par les workers de Temporal. Si une Activity échoue, Temporal peut automatiquement la relancer en fonction des politiques configurées sans réexécuter l'ensemble du Workflow depuis le début.
Implémentation pratique en Python
Examinons un exemple pratique d'un workflow de traitement de paiement utilisant le SDK Python de Temporal. Ce modèle garantit que même si le serveur redémarre entre le débit de la carte et la mise à jour de l'enregistrement de l'utilisateur, le système reste cohérent.
import time
from temporalio.client import Client
from temporalio.common import RetryPolicy
from temporalio.contrib.opentelemetry import TracingWorkflowInterceptor
from temporalio.worker import Worker
from temporalio.workflow import (
ActivityError,
define_workflow,
workflow_activity,
workflow_run,
workflow_signal,
)
# 1. Définir les Activités (Les "Effets de bord")
@workflow_activity(name="charge_credit_card")
async def charge_credit_card(amount: float, card_id: str) -> str:
"""Simuler un débit de carte de crédit."""
# Dans une application réelle, cela appelle une passerelle de paiement
if amount > 1000:
raise Exception("Paiement refusé : Limite dépassée")
return f"tx_{int(time.time())}_{card_id}"
@workflow_activity(name="update_user_balance")
async def update_user_balance(user_id: str, transaction_id: str) -> None:
"""Simuler la mise à jour du grand livre interne de l'utilisateur."""
# Dans une application réelle, cela écrit dans la base de données
print(f"Mise à jour du solde pour {user_id} avec la transaction {transaction_id}")
# 2. Définir le Workflow (L'"Orchestration")
@define_workflow(name="PaymentProcessingWorkflow")
class PaymentProcessingWorkflow:
async def __init__(self, amount: float, card_id: str, user_id: str):
self.amount = amount
self.card_id = card_id
self.user_id = user_id
async def run(self):
"""
Ce code est déterministe.
Temporal rejouera cette logique basée sur l'historique des événements.
"""
# Exécuter l'activité.
# Si cela échoue, Temporal le relance selon la politique par défaut.
transaction_id = await self._activity(
charge_credit_card,
self.amount,
self.card_id
)
# Attendre un signal (par ex. approbation manuelle) si nécessaire
# await self._signal("approval_received")
# Mettre à jour le grand livre
await self._activity(
update_user_balance,
self.user_id,
transaction_id
)
return transaction_id
# Assistant pour appeler les activités
async def _activity(self, activity_func, *args, **kwargs):
return await activity_func(*args, **kwargs)
# 3. Exécuter un Worker (C'est ce que votre service d'application exécute)
async def main():
client = await Client.connect("localhost:7233", namespace="default")
async with Worker(
client,
task_queue="payment-queue",
workflows=[PaymentProcessingWorkflow],
activities=[
charge_credit_card,
update_user_balance
]
) as worker:
print("Worker prêt")
await worker.run()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Pourquoi choisir Temporal plutôt que les alternatives ?
Bien que des systèmes comme AWS Step Functions ou Azure Durable Functions offrent des capacités similaires, ils vous enferment souvent dans des fournisseurs cloud spécifiques ou vous obligent à définir des workflows via JSON/YAML. Temporal est open-source et auto-hébergeable, vous donnant un contrôle total sur votre infrastructure. De plus, comme vous écrivez vos workflows dans des langages mainstream (Python, Go, TypeScript, Java, .NET), vous pouvez exploiter vos bases de code existantes, vos frameworks de tests unitaires et le support de votre IDE.
L'une des fonctionnalités les plus puissantes est la Visibilité. Avec Temporal, vous n'avez pas besoin de construire un tableau de bord de journalisation personnalisé pour voir l'état de vos workflows. L'interface Temporal vous permet d'inspecter n'importe quelle exécution de workflow, de voir la séquence exacte des événements, de visualiser le code exécuté et même de déclencher des signaux pour changer le comportement des workflows en cours d'exécution en temps réel.
Conclusion
Temporal résout les problèmes les plus difficiles des systèmes distribués : fiabilité, observabilité et complexité. En abstrayant la gestion d'état et le traitement des échecs, il permet aux développeurs de se concentrer sur la logique métier plutôt que sur le code de liaison d'infrastructure. Que vous construisiez un pipeline de données, un système d'exécution de commandes ou une interaction complexe de microservices, Temporal fournit une base robuste pour une automatisation scalable et résiliente. Si vous gérez des processus de longue durée, il est temps de passer au-delà des tâches cron et de commencer à penser en termes d'Exécution Durable.