|
8 min de lecture
|

File d'attente HTTP d'Aidbox : comportement, modes de défaillance et contrôle

Résumer cet article avec :
ChatGPTPerplexityClaudeGrok

L'ingestion de données est l'un des premiers grands obstacles pour quiconque adopte FHIR — que vous démarriez tout juste, que vous lanciez un nouvel environnement ou que vous migriez depuis un serveur existant. Lorsque l'ingestion est mal conçue, les équipes se heurtent à des migrations lentes, des références brisées et des systèmes qui se dégradent sous la charge.

Cet article fait partie d'une série que nous publions sur l'ingestion de données FHIR dans Aidbox. Chaque article traite d'un aspect particulier du problème. Dans celui-ci, nous nous concentrons sur la file d'attente HTTP d'Aidbox : son fonctionnement et ce qu'il faut surveiller.

Aidbox peut être considéré comme un serveur web asynchrone et non bloquant, construit autour d'une file d'attente de requêtes et d'un nombre fixe de fils d'exécution travailleurs (BOX_WEB_THREAD). Chaque requête entrante est d'abord placée dans la file d'attente, puis prise en charge pour traitement.

Lorsque le taux de requêtes entrantes dépasse le nombre de travailleurs disponibles, les requêtes commencent à s'accumuler dans la file d'attente. Le serveur cesse alors de répondre en temps réel et la latence augmente. Sous une charge soutenue, certaines requêtes peuvent ne pas être traitées à temps et échoueront éventuellement en raison des délais d'attente côté client.

La présence d'éléments dans la file d'attente HTTP indique que le système est sous pression et reçoit plus de requêtes simultanées qu'il ne peut en traiter immédiatement. Une file d'attente quasi vide correspond généralement à une faible latence, tandis qu'une file légèrement remplie peut être acceptable et même bénéfique pour une utilisation efficace des ressources. Cependant, une croissance soutenue de la longueur ou du temps d'attente dans la file est un signal fort de surcharge — cela signifie que les travailleurs sont continuellement occupés et ne parviennent pas à suivre le trafic entrant.

Pour illustrer ces dynamiques avec des métriques concrètes, nous avons élaboré trois scénarios techniques distincts. En analysant ces exemples, nous pouvons identifier des stratégies efficaces pour éviter les pièges courants et établir un modèle de communication client-serveur à haute performance. Vous trouverez toutes les recommandations pratiques à la fin de l'article.

Tous les scénarios reposent sur le même profil de charge afin d'assurer la comparabilité :

  • Jeu de données fixe de 1 200 lots (p. ex., 100 par lot)
  • La taille et la structure des lots demeurent constantes entre les tests
  • Le client envoie continuellement des requêtes jusqu'à la limite de simultanéité configurée

Cela nous permet d'isoler l'effet de la simultanéité et du comportement des délais d'attente sur la file d'attente HTTP, sans introduire de variabilité provenant de la charge elle-même.

Scénario 1 : simultanéité illimitée + court délai d'attente côté client

Dans cette configuration, Aidbox est configuré avec 8 fils d'exécution travailleurs (BOX_WEB_THREAD) fonctionnant sur un seul processeur, tandis que le client génère jusqu'à 64 requêtes simultanées avec un délai d'attente de 10 secondes. À première vue, cela peut sembler un flux de charge raisonnable, mais en pratique, cela crée un schéma de surcharge classique causé par un décalage entre la simultanéité et le comportement des délais d'attente.

Scenario 1: queue growth and timeout-driven retry storm

Parce que le taux entrant est supérieur au taux de traitement, la file d'attente commence à croître. Cela a une incidence directe sur la latence ≈ (temps d'attente en file + temps de traitement) : chaque requête doit maintenant patienter plus longtemps dans la file avant d'être prise en charge par un travailleur. Finalement, le temps d'attente dépasse le seuil de délai d'attente du client, fixé à 10 secondes.

À ce stade, le comportement suivant se produit : le client ferme la connexion et considère la requête comme échouée, mais le serveur n'annule pas le travail. La requête demeure dans la file d'attente (ou est déjà en cours de traitement) et continue de consommer des ressources jusqu'à son achèvement. Du point de vue du serveur, il effectue toujours un travail utile — mais du point de vue du client, ce travail est déjà sans intérêt.

À mesure que les délais d'attente s'accumulent, le client commence à émettre des nouvelles tentatives, ce qui génère encore plus de requêtes. Ces nouvelles requêtes s'ajoutent à la file d'attente déjà en croissance, qui contient encore du travail périmé provenant des requêtes ayant précédemment expiré. Cela crée une boucle de rétroaction positive : plus de charge entraîne plus de délais d'attente dépassés, ce qui déclenche de nouvelles tentatives et augmente encore davantage la charge.

Scénario 2 : haute simultanéité + aucun délai d'attente côté client

Dans cette configuration, la configuration du serveur demeure identique — 8 fils d'exécution travailleurs sur un seul processeur — tandis que le client continue d'envoyer jusqu'à 64 requêtes simultanées. La différence clé est que le client n'applique aucun délai d'attente, ce qui signifie qu'il est prêt à attendre indéfiniment une réponse.

Scenario 2: steady-state queue without timeouts

Comme dans le scénario 1, le taux de requêtes entrantes dépasse la capacité de traitement du serveur. Les requêtes sont placées dans la file d'attente HTTP, et les travailleurs les traitent à un rythme fixe. Cependant, comme le client n'applique pas de délai d'attente, aucune requête n'est abandonnée et aucune nouvelle tentative n'est générée.

Par conséquent, le système atteint un état stable. La file d'attente croît initialement, mais se stabilise éventuellement à un niveau approximativement égal à la différence entre la simultanéité entrante et les travailleurs disponibles (dans ce cas, environ ~56 requêtes en attente). Au lieu d'une file d'attente incontrôlable, nous avons maintenant un arriéré constant.

De l'extérieur, cela semble beaucoup plus sain : il n'y a pas de délais d'attente dépassés, pas d'erreurs et pas de tempête de nouvelles tentatives. Cependant, cette stabilité a un coût en termes de latence. Chaque requête doit attendre dans la file avant d'être traitée, ce qui augmente considérablement le temps de réponse de bout en bout.

Il est important de noter que le débit (RPS) demeure inchangé par rapport au scénario 1. Le système est toujours limité par le nombre de fils d'exécution travailleurs et la capacité du processeur. La seule chose qui a changé, c'est la façon dont la charge excédentaire est gérée : au lieu d'être abandonnée (via les délais d'attente), elle est absorbée dans la latence.

Cela signifie que le système fonctionne en saturation constante — tous les travailleurs sont continuellement occupés, et la file d'attente sert de tampon qui absorbe la surcharge au détriment de la réactivité.

Dans cette configuration en état stable, chaque requête entrante est forcée de traverser l'arriéré existant (environ ~50 requêtes en attente) avant de pouvoir être traitée. La file d'attente devient une barrière de latence pour tout le trafic.

Étant donné que la file d'attente est partagée, tous les types de requêtes, y compris les requêtes critiques telles que les vérifications de santé ou les opérations orientées utilisateur, sont soumis au même délai. Bien que les requêtes ne soient pas abandonnées, leur succès dépend de leur capacité à tolérer ce temps d'attente accumulé.

Cela crée un risque subtil mais important : le système semble stable, mais toute augmentation de la sensibilité à la latence (p. ex., l'introduction de délais d'attente ou l'ajout de trafic supplémentaire) peut rapidement le faire basculer vers un mode de défaillance.

Scénario 3 : simultanéité alignée à la capacité des travailleurs

Dans cette configuration, la simultanéité côté client est alignée sur la capacité de traitement du serveur : 8 requêtes simultanées face à 8 fils d'exécution travailleurs sur un seul processeur. Contrairement aux scénarios précédents, le système n'est plus surchargé — le taux de requêtes entrantes correspond étroitement à ce que le serveur peut traiter.

Scenario 3: matched concurrency, low latency, zero buffer

Grâce à cet alignement, les requêtes sont prises en charge par les travailleurs presque immédiatement après leur arrivée. La file d'attente HTTP demeure quasi vide, et le délai d'attente en file est effectivement éliminé. Chaque requête traverse le système avec un temps d'attente minimal, ce qui se traduit par une latence faible et prévisible.

Du point de vue du débit, le système fonctionne à sa limite naturelle : le RPS correspond à la capacité des travailleurs, et il n'y a pas d'inflation artificielle ni de dégradation causée par les nouvelles tentatives ou l'arriéré. Le système paraît stable, efficace et bien équilibré.

Cependant, cette configuration comporte une limite importante. Puisque tous les travailleurs sont continuellement occupés par le trafic client, il n'y a pas de capacité de réserve pour absorber une charge supplémentaire. Toutes les requêtes externes, telles que les vérifications de santé, les tâches en arrière-plan ou le trafic généré par les utilisateurs, entreront immédiatement en concurrence pour les mêmes travailleurs et commenceront à s'accumuler en file.

En d'autres termes, le système fonctionne à pleine utilisation, sans aucune marge tampon. Bien que cela minimise la latence dans des conditions contrôlées, cela rend le système fragile dans des environnements réels où le trafic est rarement parfaitement prévisible.

Cette configuration est proche de l'idéal en termes d'efficacité — le système traite les requêtes avec une latence minimale et sans accumulation dans la file. Cependant, il fonctionne juste à la limite de sa capacité.

Avec tous les travailleurs continuellement occupés, il n'existe effectivement aucune marge tampon pour absorber une charge supplémentaire quelconque. Même une légère augmentation du trafic ou la présence de requêtes auxiliaires poussera immédiatement le système vers la mise en file d'attente et une latence accrue.

En d'autres termes, le système est parfaitement équilibré — mais de façon très serrée, ne laissant aucune marge pour la variabilité ou les conditions du monde réel.

Modèle de charge recommandé

Ce modèle est intentionnellement simplifié et ne représente pas avec exactitude le comportement du système en pratique, mais il est utile pour développer une intuition. En réalité, un seul travailleur peut parfois traiter plus d'une requête simultanément, selon la disponibilité du processeur et la nature (taille et complexité) de la charge de travail.

En règle générale, il est utile de raisonner en termes d'alignement : le nombre de requêtes client simultanées ciblant Aidbox devrait généralement rester en dessous du nombre de fils d'exécution web configurés. Plus le système doit gérer de trafic auxiliaire ou secondaire (vérifications de santé, tâches en arrière-plan, intégrations), plus vous devriez laisser de marge — c'est-à-dire moins de requêtes simultanées côté client par rapport aux travailleurs disponibles.

Recommended load pattern: client concurrency below worker capacity

En d'autres termes, tous les travailleurs ne devraient pas être « réservés » à une seule source de trafic ; une certaine capacité devrait rester disponible pour tout ce que le système doit traiter par ailleurs.

Une configuration efficace repose sur une observabilité robuste. En surveillant des métriques clés telles que le volume de requêtes, la profondeur de la file d'attente, la latence, le RPS et les délais d'attente dépassés, vous pouvez identifier l'équilibre optimal pour votre charge de travail spécifique. Cette approche fondée sur les données vous permet d'ajuster précisément le nombre de travailleurs Aidbox et les ressources processeur allouées à votre environnement.

Nous explorerons d'autres aspects de l'évolutivité d'Aidbox et de la gestion de la charge de travail dans les prochains articles.

Comment configurer cela dans Aidbox

Si vous travaillez avec une ingestion FHIR à haut débit, vous pouvez configurer les travailleurs dans Aidbox. Cependant, l'optimisation des performances va bien au-delà de la simple augmentation du nombre de travailleurs — nous vous recommandons de consulter l'article qui explique comment les travailleurs web et les connexions à la base de données (db) contribuent ensemble aux performances d'Aidbox.

Lancez votre propre instance Aidbox en quelques secondes pour continuer à expérimenter, ou contactez-nous pour discuter de cette configuration.

Partager cet article
Comments
Comments
Sign in
Loading comments...
Subscribe to our blog

Get the latest articles on FHIR, interoperability, and healthcare IT.