HiAPI
Blog

Tutoriel

hiapi /v1/tasks : tâches suspendues ou expirées — Manuel de diagnostic et de nouvelle tentative

Manuel pratique pour développeurs : déboguer des tâches asynchrones bloquées, ajuster le rythme d'interrogation et décider quand réessayer et quand soumettre à nouveau

Oct 31, 2026
hiapi 博客封面图:错位叠放的骨白色纸张如套印偏移,边缘呈炭黑色,中间夹一张锈红色纸页,平面落地投影。

Pourquoi les tâches asynchrones se suspendent et expirent

Une tâche asynchrone sur /v1/tasks est un contrat entre trois parties : votre client, la plateforme HiAPI et la ressource de calcul qui exécute réellement le travail. Une suspension signifie presque toujours qu'une de ces couches attend une autre, et la première chose à faire en débogage est de déterminer laquelle — car chaque couche se corrige différemment, et réessayer aveuglément ne fait qu'aggraver la situation.

Le cycle de vie à garder en tête est le suivant :

Le cycle de vie, phase par phase

  1. Soumission (Submit) — votre client envoie une requête, la plateforme l'accepte et renvoie un identifiant de tâche.
  2. Mise en file (Enqueue) — la tâche est planifiée. C'est la phase qui ralentit le plus facilement sous forte charge, et celle que vous voyez le moins.
  3. Exécution (Run) — la ressource de calcul traite la tâche. La durée ici dépend entièrement de la charge de travail, et non de votre client HTTP.
  4. Achèvement (Complete) — un état terminal est enregistré et peut être lu via le même identifiant de tâche.

Une « suspension » est rarement un événement unique ; elle relève généralement de l'un des cas suivants :

  • La tâche est réellement encore en cours d'exécution. Une entrée longue, une sortie volumineuse ou une file chargée allongent la phase 3. Rien n'est cassé, vous êtes simplement trop pressé.
  • La tâche est mise en file mais n'a pas encore commencé. La phase 2 peut durer longtemps et ressembler exactement à une suspension de votre côté.
  • Votre client a abandonné trop tôt. Vous avez appliqué le délai d'expiration HTTP à l'interrogation de l'état plutôt qu'à la tâche ; la plateforme fonctionne normalement, mais votre processus s'est arrêté.
  • La tâche a atteint un état terminal, mais vous ne l'avez pas détecté. Votre boucle d'interrogation s'est arrêtée, a planté ou s'est terminée avant d'obtenir la réponse.
  • Une véritable défaillance côté plateforme. Plus rare, mais réelle, et le seul cas où l'on devrait envisager une nouvelle tentative au niveau de la plateforme.

Le piège fondamental

L'expiration auto-infligée la plus courante consiste à confondre deux horloges. La requête HTTP que vous utilisez pour interroger l'état doit être courte — ce n'est qu'une lecture. La tâche, elle, a sa propre échéance, bien plus longue. Si vous utilisez la même valeur de délai pour les deux, vous annulerez des tâches pourtant saines, puis vous les relancerez, dupliquant un travail presque terminé.

Avant de modifier quoi que ce soit, déterminez dans lequel des cinq cas ci-dessus vous vous trouvez réellement. Le reste de ce manuel dépend de cette réponse.

Étape 1 : rassembler les preuves de la tâche

Résistez à l'envie de modifier le code ou de relancer dès la première minute. Une suspension est un état qui ne se débogue qu'a posteriori, une fois que vous avez consigné suffisamment d'informations. Capturez les éléments suivants avant d'intervenir.

Ce qu'il faut consigner pour chaque tâche

  • L'identifiant de tâche renvoyé lors de la soumission. C'est la clé de tout ; sans lui, vous ne pouvez que deviner.
  • La dernière chaîne d'état observée, consignée mot pour mot, sans reformulation.
  • Les horodatages : quand vous avez soumis, quand chaque interrogation d'état a été émise, quand chaque interrogation a répondu.
  • Le nombre d'interrogations d'état effectuées et leur intervalle.
  • La forme de la charge utile de la requête — pas nécessairement le contenu complet, mais assez pour reproduire : taille d'entrée, paramètres et tout ce qui influence la durée d'exécution.
  • Le délai d'expiration côté client que vous avez défini (le cas échéant), et s'il s'est déclenché.

Interrogez à nouveau manuellement le même identifiant

Avant de tirer une conclusion, lancez une interrogation d'état toute fraîche sur le même identifiant de tâche. Deux issues possibles :

  • L'état a progressé. La tâche est vivante ; vous avez affaire à un bug d'interrogation ou d'expiration, pas à un bug de tâche. Passez à la section sur l'interrogation.
  • L'état n'a pas changé et la tâche est déjà ancienne. Vous avez maintenant une véritable suspicion de tâche bloquée. Notez depuis combien de temps elle stagne dans cet état et comparez avec vos attentes.

Consignez-le par écrit

Gardez une courte ligne de journal par tentative : identifiant, état, temps écoulé, décision prise. Les suspensions récurrentes se diagnostiquent presque toujours à partir de l'historique, et non de l'événement en direct. Si vous ne pouvez pas reconstituer ce qu'une tâche faisait il y a une heure, vous continuerez à déboguer la même panne.

Un autre élément de preuve facile à oublier : l'heure de la journée et le type de charge de travail. Si les suspensions se concentrent sur un type d'entrée donné, vous avez découvert un schéma à signaler, et non un incident sporadique à réessayer.

Interpréter correctement l'état d'une tâche

L'état est la base factuelle de toute décision de nouvelle tentative. Traitez-le comme une petite machine à états, en faisant correspondre précisément chaque état à une action, afin de ne jamais relancer une tâche encore en cours.

Les états que vous verrez et leur signification

  • Queued / pending (en file / en attente) — acceptée, non démarrée. Rester longtemps ici indique une pression de planification. Ne relancez pas ; la tâche existe déjà.
  • Running / processing (en cours / en traitement) — la ressource de calcul travaille. Aucun problème. Attendre est l'action correcte.
  • Succeeded / completed (réussie / terminée) — état terminal. Lisez le résultat, c'est fini.
  • Failed / error (échouée / erreur) — état terminal, et sa charge utile d'erreur est votre meilleur matériau de débogage. Lisez-la avant toute décision.
  • Cancelled / expired (annulée / expirée) — état terminal dû à une politique ou à votre propre action. Réessayer ou non est une décision entièrement nouvelle.
  • Unknown ou absent — identifiant non reconnu. Ce n'est pas un état d'échec, mais un état de preuve. Relisez l'identifiant et vérifiez que c'est bien le bon.

Deux questions à se poser face à tout état

  1. Est-ce un état terminal ? Sinon, la seule action correcte est de continuer à attendre dans votre budget.
  2. Si c'est un état terminal, est-il réessayable ? Un succès n'est pas réessayable. Un échec peut l'être ou non — lisez d'abord l'erreur. Un identifiant inconnu exige une enquête, pas une nouvelle tentative.

N'inférez pas l'état à partir du temps écoulé

« Elle tourne depuis dix minutes, donc elle est forcément bloquée » est une supposition, pas un diagnostic. Le temps écoulé vous indique seulement quand interroger, jamais quel est l'état. Une tâche peut rester longtemps en file, puis se terminer parfaitement. Laissez la chaîne d'état conclure ; votre horloge ne fait que décider de la fréquence de vos interrogations.

Concevoir un rythme d'interrogation raisonnable

La plupart des « suspensions » sont des bugs d'interrogation déguisés. Il existe deux modes de défaillance principaux : interroger trop agressivement, ce qui nuit à votre propre fiabilité, et interroger trop naïvement, ce qui ne détecte jamais l'état terminal. Un rythme borné résout les deux.

Démarrez lentement, augmentez doucement l'intervalle

Ne faites pas d'interrogation en boucle serrée. Commencez par un intervalle court pour que les tâches rapides aboutissent vite, puis augmentez l'intervalle à mesure que la tâche vieillit :

import time

def poll(job_id, check_status, deadline_s=600):
    start = time.monotonic()
    interval = 1.0
    while True:
        status = check_status(job_id)
        if status in ("succeeded", "failed", "cancelled", "expired"):
            return status
        if time.monotonic() - start > deadline_s:
            return "client_deadline_exceeded"
        time.sleep(interval)
        interval = min(interval * 1.5, 30.0)  # plafonné

Propriétés clés : un intervalle initial assez court pour capturer les tâches rapides, un facteur de croissance qui maintient le coût faible pour les tâches longues, et un plafond pour ne jamais dormir une heure et manquer le résultat.

Définissez toujours un délai d'expiration côté client

Ne supposez jamais que la plateforme émettra un signal d'achèvement à votre place. Fixez votre propre échéance — la durée maximale que vous êtes prêt à attendre — et traitez son dépassement comme un résultat distinct, et non comme un échec. Cette distinction est importante : le résultat client_deadline_exceeded signifie « je ne regarde plus », et non « la tâche est morte ». Consignez-le comme tel.

Deux règles pratiques

  • Limitez le nombre total d'interrogations. Une boucle sans nombre maximal d'itérations est une suspension qui attend d'arriver. Calculez-le à partir de votre échéance et du plafond d'intervalle.
  • Séparez le délai de lecture de l'échéance de la tâche. L'appel HTTP d'obtention d'état doit avoir son propre délai court. Si la lecture d'état expire, réessayez cette lecture — ne relancez jamais la tâche.

Respectez le point de terminaison

L'interrogation est bon marché, mais pas gratuite. Si vous devez vérifier des centaines de tâches, échelonnez-les et réutilisez le même client. En cas de doute, plus lent est presque toujours la direction la plus sûre : une tâche qui mérite six minutes d'attente mérite aussi six minutes et trente secondes.

Choisir entre réessayer et soumettre à nouveau

C'est la décision où la justesse et le coût se heurtent. Réessayer la même tâche et soumettre une nouvelle tâche ne sont pas interchangeables ; faire le mauvais choix duplique le travail ou laisse une tâche saine en suspens.

Règle de décision

Traitez les points suivants dans l'ordre :

  1. La tâche est-elle dans un état terminal ? Sinon, ne relancez pas et ne soumettez pas à nouveau. Attendez. Relancer une tâche encore en cours duplique le travail.
  2. Si elle est terminale, que dit l'état ? Un succès signifie que vous avez terminé. Un échec porte une charge utile d'erreur — lisez-la d'abord.
  3. L'échec est-il potentiellement temporaire ? Délai d'expiration sous forte charge, indisponibilité brève, limitation de débit — tous ces cas sont candidats à une nouvelle tentative.
  4. L'échec est-il potentiellement définitif ? Entrée invalide, paramètre non pris en charge, charge utile malformée — ils échoueront de la même manière quel que soit le nombre d'essais. Corrigez la requête.

Réessayez la même tâche lorsque…

La tâche existe, elle est passée dans un état terminal d'échec pour une raison qui ne se reproduira probablement pas, et la sémantique de la plateforme pour cette tâche permet une nouvelle tentative. Vous réutilisez l'identifiant, conservez un historique et évitez de créer une seconde tâche qui fait la même chose.

Soumettez à nouveau en tant que nouvelle tâche lorsque…

  • L'identifiant est inconnu ou n'est plus accessible — il n'y a plus rien à réessayer.
  • La tâche d'origine a explicitement expiré selon une politique, et une nouvelle tentative n'a rien à quoi s'accrocher.
  • Vous devez modifier l'entrée. Réessayer, c'est la même requête ; corriger, c'est une nouvelle requête. Ne laissez jamais une nouvelle tentative avoir un sens différent de la requête d'origine.

L'asymétrie des coûts

Une nouvelle tentative sur une tâche encore en cours double le travail pour un seul résultat. Une resoumission est une tâche nouvelle et explicite — nouvel identifiant, nouveau cycle de vie et nouveau coût. Les deux sont des outils légitimes ; les deux doivent être utilisés avec discernement. Règle empirique : réessayez en cas de doute sur l'infrastructure, soumettez à nouveau en cas de certitude sur l'entrée.

Rendre les nouvelles tentatives sûres

Une nouvelle tentative qui duplique silencieusement le travail est pire qu'une suspension, car elle échoue en silence et vous coûte de l'argent. Intégrez la sûreté avant d'en avoir besoin.

Pensez en termes d'idempotence

Pour chaque type de tâche que vous exécutez, demandez-vous : si elle s'exécute deux fois, le résultat reste-t-il correct ? Si la réponse est non — par exemple, la tâche ajoute une ligne ou déclenche un effet secondaire en aval — alors votre logique de nouvelle tentative doit être conditionnée par l'état, jamais par un minuteur. Ne relancez que les échecs terminaux, et seulement après qu'une interrogation d'état toute fraîche a confirmé cet état.

Limitez le nombre de tentatives

Chaque boucle de nouvelle tentative a besoin d'un nombre maximal de tentatives et d'une durée totale maximale, tous deux vérifiés explicitement. Une boucle de nouvelle tentative non bornée n'est qu'une suspension plus lente et plus coûteuse. Lorsque le budget est épuisé, exposez l'échec — ne l'avalez pas en silence.

Ajoutez un backoff entre les tentatives

Les tentatives elles-mêmes doivent être espacées, avec un intervalle croissant à chaque échec. Réessayer immédiatement une dépendance déjà défaillante ne fait généralement qu'échouer à nouveau et ajouter de la charge à un système déjà sollicité.

Consignez les tentatives, pas seulement les erreurs

Pour chaque tentative, consignez : l'identifiant de tâche, l'état au moment de la décision, le numéro de tentative, l'intervalle utilisé et le résultat. C'est la différence entre « elle se suspend parfois » et « elle se suspend le mardi après-midi lors du traitement d'entrées volumineuses ». Ce dernier cas est réparable, le premier ne l'est pas.

Ne masquez jamais une véritable défaillance

Si une tâche échoue trois fois et que votre couche d'encapsulation ne renvoie qu'une erreur générique, vous avez jeté les charges utiles d'erreur qui expliquaient la cause. Conservez l'état final et les détails de l'erreur jusqu'à l'appelant. Une nouvelle tentative est un mécanisme de récupération, pas de camouflage — elle doit réduire les chances que le problème sous-jacent se manifeste, pas celles qu'il soit vu.

Liste de rétrospective pour les suspensions récurrentes

Une suspension se produit une fois : corrigez la tâche concernée et passez à autre chose. Si elle se produit deux fois, déroulez cette liste — sinon vous paierez éternellement pour des nouvelles tentatives.

Liste

  • Classez l'incident. S'agit-il d'une tâche en cours que vous avez cessé d'observer, d'une tâche réellement bloquée, d'un délai d'expiration côté client, ou d'un état que vous n'avez pas su lire ? Si vous pouvez le nommer, la moitié est réglée.
  • Vérifiez le calcul d'interrogation. Votre plafond d'intervalle, votre échéance et votre limite de tentatives sont-ils réellement appliqués ? Y a-t-il une boucle non bornée ?
  • Confirmez que les délais sont séparés. Votre délai de lecture d'état a-t-il déjà été confondu avec l'échéance de la tâche ?
  • Révisez les décisions de nouvelle tentative. Une nouvelle tentative a-t-elle été lancée sur une tâche non terminale ? Une resoumission a-t-elle dupliqué une tâche encore en cours ?
  • Vérifiez les charges utiles d'échec. Les erreurs ont-elles été conservées ou avalées par la couche d'encapsulation ?
  • Cherchez des schémas dans les entrées. Les échecs se regroupent-ils selon la taille de la charge utile, les paramètres ou le type de charge de travail ?
  • Reconsidérez les attentes. La tâche dépasse-t-elle votre échéance parce que le travail est réellement aussi important ? Si oui, le bug est dans l'échéance.

Transformez les constats en configuration par défaut

Chaque point ci-dessus doit devenir une pratique par défaut dans votre client : un interrogateur borné, un résultat d'échéance distinct, une stratégie de nouvelle tentative pilotée par l'état et des journaux structurés indexés par identifiant de tâche. Faites cela, et la prochaine suspension ne sera qu'une ligne dans les journaux, pas un après-midi perdu.

Une tâche asynchrone est un contrat, et le contrat tient tant que vous respectez son état. Distinguez encore en cours de jamais terminable, attendez à un rythme borné, ne relancez que ce qui peut l'être en toute sûreté — ainsi une suspension restera un désagrément mineur plutôt que de devenir une panne.

Articles similaires

Voir tous les articles →

Commencez avec des artefacts IA persistants

Obtenez votre clé API et appelez des modèles d'image, de vidéo et d'audio via une API unique basée sur les tasks.

Lire la documentation de l'API