Votre middleware reprend le catalogue d’une boutique Shopify. Quelques minutes plus tard, les synchronisations de vos autres clients attendent aussi, alors que leurs files ne contiennent presque rien. Les journaux affichent des réponses HTTP 200. Dans le corps de l’une d’elles, GraphQL signale pourtant THROTTLED. Le connecteur a soit fermé le mauvais circuit, soit gardé ses workers occupés pendant l’attente d’un seul périmètre.
Le premier signal faible est une boutique calme dont les messages vieillissent au même rythme que le gros import voisin. Le second est un compteur de succès HTTP qui reste vert pendant que les extractions ne produisent plus les données attendues. Ces observations demandent deux enquêtes : lire correctement la réponse Shopify, puis suivre l’endroit où votre propre middleware impose l’attente.
Le risque est de transformer une contrainte locale en arrêt collectif. Le budget de coût GraphQL Admin doit être rapproché de l’application et de la boutique concernées. Votre identifiant de client, votre worker ou votre token affiché ne constituent pas automatiquement cette frontière. Le diagnostic conserve la réponse, l’identité de l’appel et le traitement différé avant d’autoriser une reprise ciblée.
Notre accompagnement en intégration API peut reprendre cette chaîne dans un middleware exploité pour plusieurs clients. L’intégration Shopify apporte le contrat propre à GraphQL Admin ; dix dossiers HTTP fictifs permettent ensuite de distinguer attente, requête trop coûteuse et erreur de droits. Ils ne remplacent pas une recette dans vos boutiques autorisées.
Pour qui : un connecteur Shopify exploité pour plusieurs boutiques
Une reprise de catalogue qui retarde des flux sans rapport avec elle
Le problème concerne les éditeurs, intégrateurs et équipes commerce dont une même application traite plusieurs boutiques. Un import volumineux peut remplir une file, mais il ne doit pas rendre invisible le retard d’un autre client. Le support commence par rapprocher boutique, application, classe de traitement et dernier résultat utilisable, plutôt que par la seule santé du serveur qui héberge les workers.
Une boutique indépendante peut encore subir un goulot interne partagé : pool de connexions, verrou global, nombre de workers ou pause générale du connecteur. L’indépendance d’un budget fournisseur ne garantit donc pas l’indépendance de votre exécution. La recette examine ces deux niveaux et nomme la ressource responsable de l’attente, au lieu de conclure que Shopify bloque tous les comptes.
Délimiter les responsabilités entre votre logiciel et le SI client
L’éditeur décrit les objets à extraire et les conséquences d’un retard. L’équipe d’intégration relève application, boutique et version d’API réellement employées. Le responsable ERP confirme, avec l’installateur du client lorsque nécessaire, ce qui peut attendre dans son circuit. Une commande non encore extraite et une commande déjà reçue par l’ERP ne demandent pas la même reprise.
La priorité est d’observer deux boutiques témoins pendant la reprise d’une troisième. Le dossier conserve leurs messages attendus, leur origine et leur délai métier. Il distingue ensuite la saturation de l’import, le partage de capacité dans le middleware et le défaut de lecture GraphQL. Corriger une seule de ces causes ne ferme pas les autres lorsqu’elles sont présentes.
Anatomie de l’attente : réponse, budget, file et worker
Relier l’appel qui a reçu THROTTLED au travail qui reste en file
La chronologie commence par l’intention du traitement : boutique interrogée, application active, lecture demandée et référence interne du lot. Elle conserve ensuite statut HTTP, corps reçu et observation de coût lorsqu’elle existe. Le message différé garde ce lien. Une file marquée « Shopify indisponible » sans application ni boutique ne permet pas de retrouver la portée du signal initial.
Le dossier suit aussi les événements intervenus après la réponse : reclassement du message, calcul d’un prochain contrôle, libération du worker et traitement des autres files. Cette suite peut révéler une pause générale ajoutée par une bibliothèque cliente ou un verrou applicatif conservé trop longtemps. Le diagnostic localise cette décision dans le connecteur sans l’attribuer au fournisseur par défaut.
Le contrat du client fixe la version dans l’URL de l’endpoint, par exemple /admin/api/2026-10/graphql.json dans la référence GraphQL examinée. Le payload transmet query et variables selon le schéma consommé. Pour une mutation, les userErrors demandées dans la réponse restent aussi à examiner ; l’absence d’erreur de premier niveau ne vaut pas validation métier. Le pilote ne réalise pas cette qualification spécifique à chaque mutation.
Observer une boutique témoin avant de changer tous les plafonds
Une boutique témoin reçoit un traitement léger dont l’équipe connaît le résultat attendu. La trace relève son passage en file, sa prise en charge et sa sortie. Si elle attend avant son premier appel Shopify, le budget distant n’explique pas directement ce délai. Le responsable cherche alors l’ordonnanceur, les workers ou le stockage partagé qui ont retenu la demande.
Augmenter la concurrence peut aggraver le problème lorsque les workers lancent davantage d’appels sur le périmètre déjà contraint. La comparaison porte donc sur les travaux réellement servis, les attentes et leurs raisons. Un débit global plus élevé ne prouve aucune amélioration pour la boutique calme qui reste derrière le catalogue en reprise.
Lire THROTTLED dans le corps d’une réponse HTTP 200
Le succès du transport ne ferme pas une opération GraphQL
La documentation Shopify sur les réponses et les erreurs, vérifiée le 12 octobre 2026, montre qu’une réponse GraphQL HTTP 200 peut contenir une erreur THROTTLED. Le connecteur lit la liste errors et le code de ses éléments. Un indicateur construit uniquement sur les statuts HTTP manque cette décision et peut fermer un traitement qui n’a pas obtenu sa sortie attendue.
Le classement conserve séparément transport reçu, erreur GraphQL observée et donnée utilisable pour l’objet demandé. Une réponse avec des données partielles exige encore leur qualification. Dans notre pilote, leur présence accompagnée d’une erreur interdit la fermeture automatique du lot. Cette règle locale ne prétend pas décrire toutes les combinaisons de réponses que Shopify produit pour toutes les opérations.
Ne pas réduire tous les refus à une attente de capacité
La référence GraphQL Admin distingue THROTTLED, MAX_COST_EXCEEDED et ACCESS_DENIED. Ces codes n’appellent pas la même action. Une requête dépassant le coût maximal doit être revue ; un refus d’accès demande une vérification des droits. Attendre quelques secondes ne corrige ni la forme de la requête ni l’autorisation qui lui manque.
Un statut HTTP 429 reste également une observation à conserver lorsqu’il apparaît. La limite examinée est ici le budget de coût calculé ; les autres limites Shopify peuvent employer un signal différent. Le contrat et la réponse effectivement reçue restent nécessaires pour qualifier une autre limite, un rejet de transport ou une restriction propre à une ressource.
Rattacher le budget à l’application et à la boutique
Deux clients de votre logiciel peuvent partager le même périmètre
La documentation du coût GraphQL Admin décrit un budget par combinaison application et boutique. Deux modules de votre logiciel peuvent donc contribuer au même budget lorsqu’ils utilisent cette combinaison. Le nom d’un module, d’une file ou d’un utilisateur interne ne crée pas une enveloppe supplémentaire chez le fournisseur.
Dans notre exemple fictif, les modules « catalogue » et « support » appartiennent à l’application A et à la boutique X. Ils gardent une attribution métier distincte, mais la clé de régulation rapproche A et X. Créer deux compteurs indépendants à partir de leurs noms locaux donnerait une capacité fictive. L’identité commerciale sert à expliquer la consommation, pas à inventer la portée du quota.
Des clés distinctes n’effacent pas les ressources communes du middleware
Une autre boutique ou une autre application possède une combinaison différente dans ce contrat de coût. La trace doit conserver cette distinction. Un circuit fermé pour A et X ne se transforme pas automatiquement en suspension de A et Y. Une politique plus large demande sa propre cause, par exemple une panne interne confirmée, et une responsabilité capable d’expliquer ce périmètre.
Les clés sont établies depuis des identités vérifiées dans votre circuit d’installation et d’authentification. Elles ne sont pas déduites d’un libellé envoyé librement avec le job. Le laboratoire reçoit ces identités comme données fournies ; il ne teste ni authentification Shopify ni autorisation multi-tenant. Cette frontière demeure à éprouver dans l’environnement réel avant de connecter des clients.
Comparer coût demandé, capacité observée et taille de réponse
Une petite réponse ne prouve pas qu’une requête peut entrer
Le budget est vérifié avant l’exécution à partir du coût demandé. Le coût réellement consommé peut ensuite être inférieur et donner lieu à restitution. Ces deux observations ont donc des rôles différents. Le responsable compare la composition de la requête et son coût demandé ; il ne dimensionne pas l’admission uniquement avec la petite réponse obtenue lors du dernier passage.
La documentation expose requestedQueryCost, actualQueryCost et throttleStatus dans les informations de coût. Le dossier conserve leur origine et l’appel concerné. Il vérifie aussi si l’observation est encore pertinente pour la prochaine demande. Plusieurs consumers peuvent avoir travaillé entre les deux ; une photographie ancienne ne constitue pas une réservation de points pour le worker qui la relit.
Traiter l’estimation d’attente comme un prochain contrôle
Notre pilote fictif reçoit un coût demandé de 60, une capacité observée de 20 et un taux de restauration de 10 points par seconde. Le déficit vaut 40 ; le calcul local propose un nouveau contrôle quatre secondes plus tard. Ces valeurs sont fournies au laboratoire, sans plan Shopify supposé. Elles illustrent une estimation, pas une date de réussite garantie par le serveur.
La reprise vérifie ensuite le contexte, la requête et les nouvelles observations. Une baisse temporaire de capacité ou un autre consumer peut modifier la situation. Si le taux fourni manque, vaut zéro ou présente une incohérence, le pilote ne fabrique pas de durée. Il conserve une attente à qualifier au lieu de lancer immédiatement une nouvelle rafale.
Exercice corrigé : dix réponses et leurs décisions de reprise
Lire de vrais échanges HTTP locaux avec des réponses fictives
Un petit serveur local renvoie les réponses préparées pour les dix dossiers. Le client les reçoit effectivement par HTTP, puis applique un classement en lecture seule. Application, boutique, horloge et informations de coût sont fournies. Aucune requête n’est envoyée à Shopify et aucune query GraphQL n’est exécutée ; le laboratoire ne calcule pas le coût natif d’une sélection de champs.
Les dossiers comparent deux boutiques, un alias métier de la première et une seconde application. Le résultat contient une décision et son périmètre. Il ne modifie aucune file réelle, ne libère aucun worker de production et ne confirme aucun objet ERP. Les échanges permettent de vérifier la lecture du signal ; l’isolation du système installé demande ensuite une recette distincte.
| Cas | Observation fournie | Décision locale | Suite à qualifier |
|---|---|---|---|
| A | HTTP 200, THROTTLED, application A et boutique X ; déficit de 40 points. | Différer la clé A/X ; contrôle estimé après quatre secondes. | Relire les observations avant une reprise réelle. |
| B | La boutique Y répond 200 avec des données fictives sans erreur. | Données candidates sur A/Y. | Vérifier la sortie métier et les attentes internes. |
| C | Un module support utilise aussi l’application A dans X. | Même clé que le dossier A. | Attribuer le module sans lui inventer un budget. |
| D | Une seconde application intervient dans la boutique X. | Clé différente de A/X. | Vérifier encore les ressources communes du middleware. |
| E | HTTP 200 contient MAX_COST_EXCEEDED. | Revoir la requête. | Réduire sa portée ou qualifier une extraction asynchrone. |
| F | HTTP 200 contient ACCESS_DENIED. | Vérifier les accès. | Relire l’installation et les droits nécessaires. |
| G | Une réponse synthétique porte HTTP 429. | Qualifier la limite HTTP. | Ne pas lui attribuer automatiquement le calcul de coût. |
| H | THROTTLED arrive sans observation de coût exploitable. | Attente sans durée inventée. | Retrouver les informations permettant le prochain contrôle. |
| I | La réponse contient des données partielles et une erreur. | Relecture de la réponse partielle. | Ne pas fermer ni remplacer un lot complet. |
| J | HTTP 504 répond à une intention de mutation fournie. | Vérifier l’état de l’écriture. | Ne pas rejouer une mutation sur la seule absence de résultat. |
Huit gardes et quatre frontières éprouvent le classement
Les huit gardes couvrent application absente, boutique absente, identité non vérifiée, type de demande inconnu, JSON mal formé, liste d’erreurs invalide, capacité booléenne et taux négatif. Ces cas restent des défauts de dossier ou de lecture. Le programme ne les transforme pas en pauses générales ni en reprises automatiques. Leur correction porte sur l’information défaillante avant tout calcul.
Les quatre frontières vérifient une capacité égale au coût demandé, un coût de 1000, un coût de 1001 et un taux nul. Le seuil de coût est issu du contrat documenté ; le délai minimal d’une seconde appartient seulement à notre pilote. Les vingt-deux échanges locaux passent et les entrées restent inchangées. Répéter le classement ne démontre aucune idempotence d’import ou de mutation.
Différer une boutique sans immobiliser tous les workers
Conserver le message et libérer sa capacité d’exécution
Une remise en attente conserve boutique, application, lot et raison du différé. Le worker peut alors traiter un autre travail admissible selon la politique de votre middleware. S’il dort en gardant une connexion ou un verrou global, les autres boutiques attendent malgré leurs clés distinctes. L’observation utile suit cette libération, puis la prise en charge du témoin qui n’était pas contraint.
La solution dépend de votre moteur de file : message différé, horodatage de disponibilité ou ordonnanceur dédié. Le livrable décrit le mécanisme réellement disponible et son comportement en cas d’arrêt du consumer. Le laboratoire HTTP n’en teste aucun. Une décision locale correctement classée peut encore être mal appliquée par le transport qui devrait la rendre opérable.
Partage du budget et accès aux workers restent deux décisions
Deux jobs de la même combinaison application/boutique demandent une régulation commune lorsque plusieurs consumers interviennent. Un compteur par processus ne suffit pas à expliquer leur consommation réunie. La recette examine réservation, concurrence et actualisation des observations dans le mécanisme choisi. Elle vérifie que deux modules locaux ne contournent pas la même contrainte en portant des noms différents.
La file donne également un passage à la boutique témoin. Cet accès peut demander une allocation ou un ordonnancement interne, indépendamment du contrat Shopify. Le responsable distingue ainsi attente imposée par le signal distant et attente ajoutée par le connecteur. Cette distinction permet de corriger une famine interne sans présenter une hausse de quota comme sa seule solution.
Réduire une lecture trop coûteuse sans promettre un bulk illimité
Revoir la requête qui ne pourra pas passer en attendant
MAX_COST_EXCEEDED impose de revoir la demande plutôt que de répéter le même appel. L’équipe relève objets, champs, pagination et sortie réellement nécessaire. Elle distingue un écran de support et une extraction catalogue complète. Une requête commune aux deux usages peut produire un coût inutile pour l’écran léger et un parcours difficile à reprendre pour l’import massif.
Le fractionnement conserve le point confirmé et les règles de rapprochement des pages. Il ne transforme pas des résultats incomplets en référentiel complet. Une query plus petite demande encore des tests sur les objets qu’elle couvre. Le support doit savoir quel périmètre est extrait et quelle preuve permet d’assembler la sortie, même lorsque la lecture demande plusieurs passages.
Qualifier l’exécution asynchrone et ses appels de contrôle
Les opérations bulk GraphQL documentées par Shopify distinguent l’exécution de la requête en masse et les appels qui la lancent ou suivent son résultat. Ces appels restent soumis à leur contrat habituel. Les limites de concurrence dépendent aussi de la version d’API ; une opération asynchrone ne constitue donc pas une enveloppe infinie.
Une extraction volumineuse garde son identité, son état et la sortie à rapprocher. Le responsable vérifie comment récupérer un résultat complet ou partiel et comment constater la fin. Il ne lance pas une seconde extraction uniquement parce qu’une notification manque. Ce chantier peut résoudre le coût d’une lecture massive, mais ne corrige pas automatiquement le verrou interne qui retient les autres boutiques.
Erreurs fréquentes : corriger un quota qui n’est pas la cause
Multiplier les tokens ou les files pour fabriquer de la capacité
Changer un libellé local ou séparer deux files peut améliorer l’attribution du travail. Cela ne suffit pas à créer une nouvelle combinaison application/boutique. Le diagnostic vérifie le contrat réel avant toute promesse de capacité. Il conserve les mêmes identités lorsqu’un module support et un module catalogue utilisent l’application installée dans une boutique commune.
Une autre erreur consiste à réduire uniquement le nombre d’appels sans examiner leur coût demandé. Une requête large peut conserver le même défaut après diminution de fréquence. Le responsable rapproche demande, observation de coût et conséquence métier, puis choisit entre révision de la query et attente ciblée. Le taux de succès HTTP ne départage pas ces deux corrections.
Rejouer une écriture ou fermer un lot sur un signal incomplet
Le dossier J fournit une intention de mutation et une réponse HTTP 504 synthétique. Le classement demande de vérifier l’état ; il n’exécute pas cette vérification. Dans votre intégration, une absence de réponse exploitable ne suffit pas à prouver l’absence d’effet. La reprise doit connaître l’objet, le contrat de l’opération et les éléments permettant de confirmer ce qui a réellement été appliqué.
Le dossier I interdit de fermer un lot seulement parce que des données figurent dans le corps. Une projection de référentiel peut autrement interpréter les absences comme des suppressions. L’équipe sépare réception, qualification de la sortie et publication vers la cible. Le refus de fermeture reste attaché à sa cause, plutôt que masqué par une nouvelle tentative générale.
Plan d’action : prouver la reprise sur une boutique et deux témoins
Conserver les observations qui permettront de fermer l’incident
Le pilote choisit une reprise catalogue et deux boutiques témoins. L’instrumentation rattache chaque appel à l’application, la boutique, la version d’API, la query et la classe de file. La journalisation conserve codes GraphQL et observations de coût utiles sans exposer les secrets. Les responsabilités distinguent préparation de la demande, classement de la réponse et décision de reprise dans l’ordonnanceur.
Le monitoring suit les attentes de chaque témoin, les clés différées et le travail effectivement servi. Le seuil d’arrêt est une suspension d’un autre périmètre sans cause qualifiée, une fermeture sur erreur ou une écriture relancée sans preuve suffisante. Le repli suspend le pilote concerné et revient à la configuration connue, avec les messages en attente conservés et leur raison lisible.
Décider l’extension avec une attente expliquée et une sortie contrôlée
La recette réelle provoque le signal sur le périmètre autorisé, puis vérifie que les témoins obtiennent leur sortie dans leur délai convenu. Elle teste aussi deux consommateurs de la même clé et l’arrêt d’un worker. Le runbook explique comment retrouver la réponse, revoir une query et reprendre le message différé. Les dépendances au moteur de file et au SDK sont nommées avant l’extension.
Exemple concret, entièrement fictif : douze synchronisations témoins demandent chacune cinq minutes d’enquête lorsqu’une pause générale brouille leur cause, soit une heure de qualification cumulée. Le coût caché porte ici sur le diagnostic des clients non concernés. Ce calcul ne représente aucun gain observé ; il aide à comparer une correction de portée et une augmentation de capacité qui laisserait le même défaut.
- À vérifier d’abord : l’erreur GraphQL et l’identité application/boutique de son appel.
- À corriger ensuite : la portée de la pause et la capacité retenue dans le middleware.
- À tester avant extension : les boutiques témoins et les consommateurs partageant la même clé.
- À refuser : une réussite déduite du seul HTTP 200 ou un rejeu collectif sans cause établie.
Lectures complémentaires : coût Shopify et capacité interne
Rattacher l’incident au contrat complet du connecteur
Le cadrage de l’intégration Shopify GraphQL, commandes et webhooks relie version, objets et preuves de sortie. Il complète le diagnostic lorsque la réponse est correctement lue mais que les données n’atteignent pas le SI attendu. La fermeture du signal de quota ne confirme pas, à elle seule, la livraison des commandes ou de l’inventaire.
La lecture du rate limiting pour protéger ERP et CRM aide à décider quelles classes de travail peuvent attendre. Cette politique reste nécessaire à l’intérieur d’une même combinaison application/boutique. Elle ne change pas la portée documentée du budget distant pour donner artificiellement une enveloppe à chaque flux.
Examiner une attente qui précède l’appel au fournisseur
La méthode de reprise des synchronisations sous contrainte de quota décrit files et signaux de transport. Elle prolonge la construction du message différé et de sa raison. Le présent diagnostic ajoute la lecture GraphQL sous HTTP 200 et la distinction entre vos libellés locaux et l’identité du budget Shopify.
Le diagnostic du voisin bruyant dans une plateforme multi-tenant traite la capacité commune d’un opérateur marketplace. Son modèle concerne un autre rôle métier, mais aide à reconnaître workers et ressources internes captés par une charge. Cette enquête reste utile lorsque la boutique témoin attend avant même d’avoir envoyé un appel Shopify.
Conclusion : une attente Shopify doit garder sa portée et sa cause
Un HTTP 200 ne suffit pas à fermer une opération GraphQL. Le classement relit les erreurs du corps, distingue attente de capacité, coût excessif et accès refusé, puis conserve l’application et la boutique concernées. Cette chaîne permet de retrouver pourquoi le traitement attend, au lieu de ranger tous les comptes derrière une indisponibilité générale supposée.
La portée du budget et celle de vos ressources internes sont deux observations différentes. Les modules d’une même combinaison doivent être rapprochés, tandis que la boutique témoin demande une cause propre lorsqu’elle est retenue. Les vingt-deux échanges fictifs vérifient un classement local ; ils ne prouvent ni restauration réelle du quota ni isolation native de vos workers.
Paradoxalement, ajouter des workers peut prolonger le défaut si leurs appels repartent sur la même clé ou s’ils gardent les verrous communs pendant l’attente. La recette réelle suit les témoins, les consumers et la sortie métier. Elle donne à la reprise une preuve de portée avant de chercher un débit plus élevé.
Reprendre le contrat Shopify et le mécanisme qui applique l’attente
Notre prestation d’intégration Shopify peut rapprocher version GraphQL, SDK, clés de régulation et moteur de file. Nous partons d’une réponse et de deux boutiques témoins pour qualifier le classement, les ressources retenues et les contrôles nécessaires à la reprise.
Dawap accompagne cette démarche en intégration API, avec les équipes de votre logiciel et les responsables du SI client. Le livrable doit permettre d’expliquer quel périmètre attend, quelle cause l’a retenu et quelle observation autorise son prochain passage, sans suspendre par défaut les autres clients.