|
7 min de lecture
|

Modèle unifié d'opérations asynchrones FHIR

Résumer cet article avec :
ChatGPTPerplexityClaudeGrok

Certaines opérations sont intensives en calcul et peuvent prendre des minutes ou des heures (exportation en masse, réindexation des paramètres de recherche, revalidation complète des ressources). Les exécuter de façon asynchrone évite de bloquer le client et permet au serveur de gérer la charge. Dans un flux asynchrone, le client lance la tâche, le serveur l'exécute en arrière-plan, et le client interroge périodiquement l'état ou reçoit un rappel à la fin de l'exécution.

FHIR offre déjà une prise en charge asynchrone par l'intermédiaire de Bulk Data Access et du modèle de requête d'interaction asynchrone R5. Ces modèles partagent le même cycle de vie :

  • Requête de démarrage -> 202 Accepted avec Content-Location.

  • Requête d'état (en cours d'exécution) -> 202 Accepted avec Retry-After et, facultativement, X-Progress.

  • Annulation facultative -> DELETE vers l'URL d'état retourne 202 Accepted.

  • État terminal -> soit 200 OK (charge utile intégrée) soit 303 See Other (redirection vers la charge utile).

Les modes intégrés (Bundle, manifest en masse) retournent toujours 200 OK à la fin ; le succès ou l'échec est indiqué par la charge utile (p. ex., un Bundle de résultats ou un OperationOutcome, ou des liens d'erreur dans un manifest). Le mode de redirection expose les codes d'état synchrones d'origine lors de la requête redirigée (p. ex., 200/201 en cas de succès, 4XX/5XX en cas d'échec).

Les modèles se distinguent principalement par l'enveloppe de la charge utile finale :

  • Bulk Data Access retourne un manifest JSON personnalisé.
  • La requête d'interaction asynchrone retourne un Bundle FHIR.
  • Le mode de redirection retourne ce que l'interaction synchrone aurait retourné (Parameters, Bundle, Binary, etc.).

Cette approche atteint ses limites lorsque la charge utile ne peut pas être exprimée sous forme de Bundle (p. ex., sorties en continu ou contenu binaire).

Les travaux R6 visent à normaliser les interactions asynchrones pour l'ensemble des interactions. Chaque interaction permet désormais 202 Accepted, et le projet de spécification de Josh propose un modèle de redirection supplémentaire : la réponse d'état terminal est 303 See Other avec un Location pointant vers le résultat ; ce point d'accès retourne la charge utile réelle et son état.

Dans l'idéal, ces modèles peuvent être unifiés en un seul modèle paramétré. On utilise Prefer: respond-async ainsi qu'un jeton Prefer personnalisé pour demander le mode. Pour la rétrocompatibilité, la valeur par défaut est bundle ; si _outputFormat est présent, la requête est traitée comme une requête en masse et un manifest est retourné (les serveurs PEUVENT rejeter un async-mode conflictuel avec 400 Bad Request).

GET /fhir/[$operation] HTTP/1.1
Prefer: respond-async, async-mode=[redirect|bundle]   # async-mode is a proposed extension token

Les serveurs DEVRAIENT confirmer les préférences honorées via Preference-Applied.

Les clients devraient supposer que les jetons Prefer inconnus sont ignorés et être prêts à revenir au mode bundle par défaut.

Lorsque l'opération est terminée :

  • async-mode=bundle : le serveur DEVRAIT répondre avec 200 OK et un corps Bundle ; les échecs utilisent une charge utile OperationOutcome.

  • Paramètre _outputFormat présent : le serveur DEVRAIT répondre avec 200 OK et un corps Bulk Manifest ; les échecs sont indiqués par des liens d'erreur dans le manifest et/ou une entrée OperationOutcome.

  • async-mode=redirect : le serveur DEVRAIT répondre avec 303 See Other et une URL Location pointant vers le résultat de l'opération ; la requête redirigée retourne les mêmes codes d'état que l'interaction synchrone.

Les clients peuvent déduire la fin de l'opération et le mode à partir du code d'état (200 ou 303) ; le succès ou l'échec dépend de la charge utile finale ou de l'état de la réponse redirigée.

Extensions

Voici une liste d'extensions supplémentaires pouvant être ajoutées pour améliorer la prise en charge des opérations asynchrones :

Extension d'interrogation longue

Les serveurs peuvent utiliser l'interrogation longue pour les requêtes d'état afin de minimiser la latence. Le serveur maintient la connexion ouverte jusqu'à ce que l'opération soit terminée ou qu'un délai d'attente soit atteint. En cas d'expiration, le client réessaie, et le serveur contrôle la fréquence des requêtes d'état entrantes. L'interrogation longue réduit le trafic d'interrogation inutile tout en assurant une notification rapide lors des changements d'état (fin ou annulation).

Extension de rappel

Objectif : permettre au serveur d'informer le client lorsqu'une tâche se termine, afin que le client puisse mettre en pause ou arrêter l'interrogation.

Motivation : les rappels maintiennent une faible latence de réponse sans interrogation intensive. Les clients peuvent ralentir leur rythme d'interrogation à des intervalles lents et peu coûteux (voire aucun) tout en recevant une notification quasi en temps réel à la fin de la tâche.

Flux :

  • Le client inclut callback-url avec Prefer: respond-async (et, facultativement, async-mode).

  • Lorsque la tâche se termine ou est annulée, le serveur envoie un seul POST à cette URL.

  • Si le rappel échoue ou n'arrive jamais, le client se fie à l'interrogation.

Exemple de démarrage avec rappel :

POST /fhir/$operation HTTP/1.1
Prefer: respond-async, async-mode=redirect, callback-url=https://example.com/callback
Accept: application/fhir+json
Content-Type: application/fhir+json

{ ...normal operation body... }

Charge utile : une ressource FHIR Parameters contenant :

  • status (completed | failed | cancelled)

  • resultUrl (URL que le client peut interroger pour obtenir la charge utile finale)

  • Paramètre OperationOutcome facultatif en cas d'échec de la tâche

Livraison : selon le principe du meilleur effort, sans nouvelle tentative ; les rappels sont un signal, non le canal de livraison principal (l'interrogation l'est). Les rappels DEVRAIENT être idempotents afin que les doublons soient sans conséquence.

Sécurité : les serveurs DEVRAIENT authentifier les rappels (p. ex., jeton OAuth2 Bearer, mTLS ou HMAC).

Traitement côté client : répondre avec 2XX en cas de succès ; un code non-2XX signifie simplement que le client procédera par interrogation.

Exemple de requête de rappel :

POST https://example.com/callback HTTP/1.1
Content-Type: application/fhir+json
Authorization: Bearer eyJhbGciOi...

{
  "resourceType": "Parameters",
  "parameter": [
    { "name": "status", "valueCode": "completed" },
    { "name": "resultUrl", "valueUrl": "https://fhir.example.com/whatever/path/1ab7162f-result" }
  ]
}

Exemples de flux visuels

Voici quelques exemples de flux visuels pour les différents modes.

Mode Bulk Manifest

Mode Bundle

Mode Redirect

Flux d'annulation

Mode Callback

Des questions ? Posez-les à notre directeur technique

Si vous avez d'autres questions sur ce modèle, n'hésitez pas à communiquer directement avec notre directeur technique, Nikolai Ryzhikov. Nous sommes toujours ravis de discuter de FHIR et de mises en œuvre concrètes.

Voir aussi : Abonnements thématiques : 5 cas d'utilisation principaux et Création de microservices en santé.

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

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