Introduction
L'un des problèmes les plus courants lors de l'utilisation de Python's asyncio est la coordination de tâches concurrentes autour d'un état partagé. La bibliothèque standard fournit asyncio.Event et asyncio.Condition, mais chacun de ces primitives a des limites qui ne sont visibles que sous une pression de concurrence réelle.
Contexte Technique
Les développeurs d'Inngest ont rencontré ce problème lors de la construction de leur SDK Python, où plusieurs gestionnaires asynchrones coordonnent autour de l'état d'une connexion WebSocket. Les états de la connexion sont les suivants : déconnecté, en train de se connecter, connecté, en train de fermer et fermé. Un des gestionnaires concurrents doit attendre que la connexion commence à se fermer pour éliminer les requêtes en attente.
Les primitives asyncio proposées par la bibliothèque standard sont les suivantes : asyncio.Event et asyncio.Condition. Cependant, chacune de ces primitives a des limites. Par exemple, asyncio.Event est un événement booléen qui ne peut être utilisé que pour un seul état, tandis que asyncio.Condition permet aux consommateurs d'attendre des prédicats arbitraires mais peut perdre des mises à jour si les transitions sont rapides.
Analyse et Implications
L'analyse de ces primitives montre que chacune d'elles a des compromis importants. La méthode de polling est inefficace et peut introduire des retards, tandis que l'utilisation d'asyncio.Event peut nécessiter plusieurs événements et une logique complexe pour gérer les transitions d'état. Asyncio.Condition, bien qu'amélioré, peut toujours perdre des mises à jour si les transitions sont rapides.
Les implications de ces limites sont importantes, car elles peuvent entraîner des bugs et des comportements inattendus dans les applications concurrentes. Il est donc essentiel de trouver une solution qui puisse gérer correctement les transitions d'état et les consommateurs asynchrones.
Perspective
La solution proposée consiste à utiliser des files d'attente par consommateur pour buffer les transitions d'état. Chaque consommateur enregistre sa propre file d'attente et reçoit les transitions d'état dans l'ordre. Cette approche permet de garantir que les consommateurs ne manquent jamais d'état et peuvent gérer correctement les transitions rapides.
La mise en œuvre de cette solution nécessite cependant certaines fonctionnalités supplémentaires pour la rendre prête pour la production, telles que la sécurité thread, l'enregistrement atomique et la typage générique.