Article Iryna Mykhailova · Nov 5, 2025 6m read

Salut!

C'est encore moi 😁. Dans l'article précédent Comment écrire un service API REST pour exporter le paquet FHIR généré au format JSON, nous avons généré une ressource DocumentReference, dont le contenu était encodé en Base64

Question!! Est-il possible d'écrire un service REST pour le décoder? Je voudrais vraiment savoir ce que contiennent les données du message🤔🤔🤔

Bien, allons-y!

1. Créez une nouvelle classe utilitaire datagen.utli.decodefhirjson.cls pour décoder les données contenues dans DocumentReference
 

Class datagen.utli.decodefhirjson Extends%RegisteredObject
{
}

2. Écrivez une fonction Python decodebase64docref pour 
a. parcourir le paquet FHIR
b. découvrir la ressource DocumentReference
  - récupérer le premier élément dans le contenu
   - récupérer la pièce jointe à partir de  contenu
     - récupérer les données à partir de la pièce jointe
c. décoder les données par Base64
 (pour cette partie, j'ai demandé l'aide de Chat-GPT😂🤫) 

Class datagen.utli.decodefhirjson Extends%RegisteredObject
{

ClassMethod decodebase64docref(fhirbundle = "") As%String [ Language = python ] { # w##class(datagen.utli.decodefhirjson).decodebase64docref() import base64 import json

def decode_discharge_summary(bundle_json):
    <span class="hljs-string">"""
    Extracts and decodes the Base64-encoded discharge summary note
    from a FHIR Bundle containing a DocumentReference.
    """</span>
    <span class="hljs-keyword">for</span> entry in bundle_json.get(<span class="hljs-string">"entry"</span>, []):
        resource = entry.get(<span class="hljs-string">"resource"</span>, {})
        <span class="hljs-keyword">if</span> resource.get(<span class="hljs-string">"resourceType"</span>) == <span class="hljs-string">"DocumentReference"</span>:
            # Traverse to the attachment
            content_list = resource.get(<span class="hljs-string">"content"</span>, [])
            <span class="hljs-keyword">if</span> not content_list:
                <span class="hljs-keyword">continue</span>
            attachment = content_list[<span class="hljs-number">0</span>].get(<span class="hljs-string">"attachment"</span>, {})
            base64_data = attachment.get(<span class="hljs-string">"data"</span>)
            <span class="hljs-keyword">if</span> base64_data:
                decoded_text = base64.b64decode(base64_data).decode(<span class="hljs-string">"utf-8"</span>)
                <span class="hljs-keyword">return</span> decoded_text
    <span class="hljs-keyword">return</span> None


# Example usage
# Load your FHIR Bundle JSON from a file or object
#with <span class="hljs-keyword">open</span>(<span class="hljs-string">"fhir_bundle.json"</span>, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> f:
#    fhir_bundle = json.loads(f)
<span class="hljs-keyword">if</span> fhirbundle==<span class="hljs-string">""</span>:
    rtstr=f<span class="hljs-string">"&#x26a0;&#xfe0f; No input found - JSON string is required."</span>
    jsstr={<span class="hljs-string">"operation_outcome"</span> : rtstr}
    <span class="hljs-keyword">return</span> json.dumps(jsstr, indent=<span class="hljs-number">2</span>)

fhir_bundle = json.loads(fhirbundle)
decoded_note = decode_discharge_summary(fhir_bundle)

<span class="hljs-keyword">if</span> decoded_note:
    #<span class="hljs-keyword">print</span>(<span class="hljs-string">"&#x1f4dd; Decoded Discharge Summary:\n"</span>)
    #<span class="hljs-keyword">print</span>(decoded_note)
    rtstr=f<span class="hljs-string">"&#x1f4dd; Decoded Discharge Summary:\n {decoded_note}"</span>
<span class="hljs-keyword">else</span>:
    #<span class="hljs-keyword">print</span>(<span class="hljs-string">"&#x26a0;&#xfe0f; No DocumentReference with Base64 note found."</span>)
    rtstr=f<span class="hljs-string">"&#x26a0;&#xfe0f; No DocumentReference with Base64 note found."</span>
jsstr={<span class="hljs-string">"data"</span> : rtstr}
<span class="hljs-keyword">return</span> json.dumps(jsstr, indent=<span class="hljs-number">2</span>)

}

}

Pour tester cette fonction, j' essaie une astuce qui consiste à utiliser la fonction genfhirbundle pour générer un paquet FHIR dans une chaîne JSON,  comme expliqué dans l'article précédent Comment écrire un service API REST pour exporter le paquet FHIR généré au format JSON 

Générons un paquet FHIR et stockons-le dans une variable jsonstr

set jsonstr=##class(datagen.utli.genfhirjson).genfhirbundle(1)

Testons la fonction de décodage decodebase64docref  avec jsonstr

w##class(datagen.utli.decodefhirjson).decodebase64docref(jsonstr)

Bien 😉 Ça semble correct. Je peux désormais lire le message décodé.


Maintenant,  revenez à l'article précédent Comment écrire un service API REST pour exporter les données patient générées au format .csv

Nous aimerions ajouter une nouvelle fonction et mettre à jour le chemin d'accès pour la classe datagen.restservice .

1. Ajoutez une nouvelle fonction DecodeDocRef, qui est censée traiter le paquet FHIR  joint dans le corps au format JSON.

Par exemple, dans ce cas, nous prévoyons un POST.

Le contenu du corps est packagé par défaut sous la forme %CSP.BinaryStream et stocké dans la variable %request.Content, nous pouvons donc utiliser la fonction .Read()  à partir de la classe %CSP.BinaryStream pour lire le BinaryStream

ClassMethod DecodeDocRef() As%Status
{
    // récupération du corps - chaîne json#dim bistream As%CSP.BinaryStream =""set bistream=%request.Contentset jsstr=bistream.Read()
<span class="hljs-comment">//décodage des données de référence du document</span>
<span class="hljs-keyword">w</span> <span class="hljs-keyword">##class</span>(datagen.utli.decodefhirjson).decodebase64docref(jsstr)
<span class="hljs-keyword">return</span> <span class="hljs-built_in">$$$OK</span>

}

2. Ensuite, nous ajoutons un chemin d'accès pour le service REST et compilons😀

<Route Url="/decode/docref" Method="POST" Call="DecodeDocRef" />

La classe  datagen.restservice mise à jour se présentera comme suit.


Génial!! C'est tout ce qu'il y a à faire!😁

Nous allons vérifier cela dans Postman!!

COLLEZ le chemin d'accès  suivant

localhost/irishealth/csp/mpapp/decode/docref

avec le corps suivant (j'ai utilisé un paquet FHIR simplifié, vous pouvez tester le paquet complet😀)

{
    "resourceType": "Bundle",
    "type": "transaction",
    "id": "98bfce83-7eb1-4afe-bf2b-42916512244e",
    "meta": {
        "lastUpdated": "2025-10-13T05:49:07Z"
    },
    "entry": [
        {
            "fullUrl": "urn:uuid:5be1037d-a481-45ca-aea9-2034e27ebdcd",
            "resource": {
                "resourceType": "DocumentReference",
                "id": "5be1037d-a481-45ca-aea9-2034e27ebdcd",
                "status": "current",
                "type": {
                    "coding": [
                        {
                            "system": "http://loinc.org",
                            "code": "18842-5",
                            "display": "Discharge summary"
                        }
                    ]
                },
                "subject": {
                    "reference": "9e3a2636-4e87-4dee-b202-709d6f94ed18"
                },
                "author": [
                    {
                        "reference": "2aa54642-6743-4153-a171-7b8a8004ce5b"
                    }
                ],
                "context": {
                    "encounter": [
                        {
                            "reference": "98cd848b-251f-4d0b-bf36-e35c9fe68956"
                        }
                    ]
                },
                "content": [
                    {
                        "attachment": {
                            "contentType": "text/plain",
                            "language": "en",
                            "data": "RGlzY2hhcmdlIHN1bW1hcnkgZm9yIHBhdGllbnQgOWUzYTI2MzYtNGU4Ny00ZGVlLWIyMDItNzA5ZDZmOTRlZDE4LiBEaWFnbm9zaXM6IFN0YWJsZS4gRm9sbG93LXVwIGluIDIgd2Vla3Mu",
                            "title": "Discharge Summary Note"
                        }
                    }
                ]
            },
            "request": {
                "method": "POST",
                "url": "DocumentReference"
            }
        }
    ]
}

Ça marche parfaitement!!!😆😉

Merci infiniment à tous d'avoir pris le temps de lire cet article. 😘

0
0 10
Article Iryna Mykhailova · Nov 3, 2025 13m read

Bonjour à tous,

Continuons à travailler sur la génération de données de test et l'exportation des résultats via une API REST. 😁

Ici, je souhaite réutiliser la classe `datagen.restservice` créée dans l'article précédent : « Écriture d'un service API REST pour exporter les données patient générées au format .csv ».

Cette fois-ci, nous prévoyons de générer un bundle FHIR incluant plusieurs ressources pour tester le référentiel FHIR.

Voici une référence si vous souhaitez en savoir plus sur FHIR : « The Concept of FHIR: A Healthcare Data Standard Designed for the Future ».

C'est parti ! 😆

0
0 10
Article Iryna Mykhailova · Oct 23, 2025 6m read

Salut,

C'est moi encore 😁. Je travaille actuellement à la génération de fausses données patients à des fins de test avec Chat-GPT et Python. J'aimerais également partager mon apprentissage. 😑

Tout d'abord, créer un service d'API REST personnalisé est facile en utilisant %CSP.REST.

Commençons ! 😂

1. Créez une classe datagen.restservice qui étend %CSP.REST.

Class datagen.restservice Extends%CSP.REST
{
Parameter CONTENTTYPE = "application/json";
}

2. Ajoutez une fonction genpatientcsv() pour générer les données du patient et les regrouper dans une chaîne csv

0
0 18
Article Iryna Mykhailova · Oct 21, 2025 2m read

Bonjour,

Je voulais partager avec vous une méthode pratique qui m'a été utile lors de mes développements sur Health Connect Cloud avec VS Code et GitBash. Lors de ces développements, si des modifications sont effectuées directement sur le serveur, comme des règles de routage ou des déploiements de composants, elles ne sont pas automatiquement incluses dans le contrôle de code source. Vous devez donc exporter les modifications depuis le serveur vers vos fichiers locaux et les envoyer vers votre dépôt distant. Je suis sûr qu'il existe des méthodes plus simples pour gérer ce problème, que je suis en train de tester, mais pour une solution rapide, j'ai pensé qu'il serait utile d'utiliser une méthode de pré-validation qui déclenche un rappel dans GitBash – voir ci-dessous.

0
0 16
Article Iryna Mykhailova · Oct 20, 2025 2m read

Rubrique FAQ InterSystems

Dans InterSystems IRIS, vous pouvez créer des tables liées à l'aide de commandes, au lieu d'utiliser System Explorer > SQL > Wizard > Linked Tables dans le Portail de gestion :

Pour créer une table liée, utilisez la méthode CreateLinkedTable de la classe %SYSTEM.SQL.Schema. Consultez la référence de la classe pour plus de détails.

Pour l'exécuter, procédez comme suit :

0
0 15
Article Iryna Mykhailova · Oct 17, 2025 1m read

Rubrique FAQ InterSystems

Certaines données, telles que les données du journal d'exécution, ne doivent pas être restaurées à leur état antérieur, même en cas de restauration lors d'une transaction. Pour ce faire, placez ces données dans la base de données IRISTEMP, qui ne sera pas restaurée.

Temporary Globals and the IRISTEMP Databas

0
0 15
Article Iryna Mykhailova · Oct 14, 2025 9m read

Dans mon article précédent, « Utilisation de LIKE avec des variables et des modèles dans SQL », nous avons exploré le comportement du prédicat LIKE dans différents scénarios, de l'Embedded SQL au Dynamic SQL, et l'impact sur les performances lorsque des caractères génériques et des variables entrent en jeu. Cet article visait à se familiariser avec l'écriture d'une requête LIKE fonctionnelle. Mais écrire du SQL efficace n'est que le point de départ. Pour créer des applications fiables, évolutives et sécurisées, vous devez comprendre les bonnes pratiques qui sous-tendent tout SQL, y compris les requêtes utilisant LIKE.

Cet article franchit une nouvelle étape. Nous aborderons quelques points clés pour renforcer votre code SQL, éviter les pièges courants et garantir l'exécution correcte, efficace et sécurisée de vos instructions SELECT. J'utiliserai les instructions SELECT avec le prédicat LIKE comme exemple, montrant comment ces principes généraux affectent directement vos requêtes et leurs résultats.

0
0 21
Article Iryna Mykhailova · Oct 9, 2025 6m read

Aperçu

Le projet typeorm-iris fournit une prise en charge expérimentale pour l'intégration de TypeORM avec InterSystems IRIS, permettant aux développeurs d'interagir avec IRIS à l'aide des décorateurs et des abstractions de référentiel bien connus de TypeORM. Cela offre une expérience de développement plus familière aux développeurs JavaScript et TypeScript qui créent des applications Node.js avec IRIS comme base de données backend.

Aperçu de TypeORM MongoDB. J'ai récemment commencé à utiliser TypeORM dans… | Eliezer  Steinbock | Medium

Bien que le projet mette en œuvre des éléments clés d'intégration avec TypeORM et prenne en charge les opérations de base sur les entités, il n'a pas encore été testé en conditions réelles et n'est pas adapté aux environnements de production.

Pourquoi typeorm-iris?

Le pilote officiel InterSystems IRIS Node.js ne permet pas d'exécuter des requêtes SQL natives , contrairement à d'autres pilotes de base de données (par exemple, PostgreSQL ou MySQL). Au lieu de cela, vous devez utiliser une API basée sur ObjectScript (par exemple, %SQL.Statement) pour préparer et exécuter les commandes SQL.

Cela pose problème lors du développement d'applications modernes qui s'appuient sur des outils de mappage objet-relationnel (ORM) tels que TypeORM. TypeORM nécessite un pilote de niveau inférieur capable de préparer et d'exécuter du code SQL brut dans une seule session de connexion, ce qui n'est actuellement pas possible avec les outils JavaScript d'IRIS.

Pour surmonter ces limitations, typeorm-iris implémente les éléments nécessaires pour faire le pont entre IRIS et TypeORM, en utilisant les interfaces d'exécution SQL ObjectScript disponibles en arrière-plan.

Phase initiale & problèmes connus

Ce projet en est à la phase initiale et n'a été testé que sur quelques cas. Il faut s'attendre à une certaine instabilité, à des fonctionnalités manquantes et à des changements importants dans les prochaines versions.

Les limitations importantes observées au cours du développement sont les suivantes:

1. Allers-retours excessifs en réseau

L'exécution de SQL via la classe %SQL.Statement à partir de JavaScript implique plusieurs messages réseau entre le processus Node.js et le serveur IRIS. Par exemple, une seule opération SQL logique peut nécessiter plusieurs étapes, telles que:

  • Preparation de l'instruction
  • Exécution de la requête
  • Récupération des métadonnées
  • Récupération de lignes de manière individuelle

Chacun d'entre eux peut générer des messages distincts sur le réseau, ce qui entraîne une surcharge considérable par rapport à l'utilisation d'un pilote SQL natif.

2. Aucune prise en charge de l'asynchronisme/du parallélisme véritable

Le pilote IRIS Node.js officiel ne prend pas en charge l'utilisation asynchrone dans un contexte multithread ou basé sur le travail:

  • La reconnexion dans le même processus échoue souvent ou provoque un comportement imprévisible.
  • La création de threads de travail et l'utilisation du pilote à l'intérieur de ceux-ci entraînent des problèmes.
  • Que une seul connexion par processus fonctionne de manière fiable.

Ces contraintes rendent ce système inadapté aux applications Node.js modernes qui traitent des tâches simultanées. Cette situation limite considérablement la capacité du pilote à s'adapter à des charges de travail simultanées et restreint considérablement les choix architecturaux.

Guide d'utilization

En raison de l'utilisation des dernières fonctionnalités IRIS SQL, il est nécessaire de disposer d'IRIS 2025.1+ pour fonctionner.

Vous pouvez installer typeorm-iris avec npm:

npm install typeorm-iris

Comme ce pilote n'est pas officiellement pris en charge par TypeORM, son utilisation nécessite une solution de remplacement pour configurer le DataSource. Vous ne pouvez pas utiliser directement new DataSource() ou createConnection() comme vous le feriez avec les pilotes officiels.

Configuration personnalisée de la source de données

import { IRISDataSource, IRISConnectionOptions } from "typeorm-iris"

const dataSourceOptions: IRISConnectionOptions = { name: "iris", type: "iris", host: "localhost", port: 1972, username: "_SYSTEM", password: "SYS", namespace: "USER", logging: true, dropSchema: true, }

export function createDataSource(options: any): IRISDataSource { // @ts-ignore const dataSource = new IRISDataSource({ ...dataSourceOptions, ...options }) return dataSource }

Lorsque l'initialisation est terminée, vous pouvez utiliser les décorateurs TypeORM comme d'habitude:

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity() export class User { @PrimaryGeneratedColumn() id: number

@Column()
name: string

@Column()
email: string

}

Il en va de même pour les référentiels:

const userRepository = dataSource.getRepository(User)
const newUser = userRepository.create({ name: "Alice", email: "alice@example.com" })
await userRepository.save(newUser)

Exemples de projets

Le référentiel GitHub comprend un dossier sample/ qui contient plusieurs exemples fonctionnels:

  • sample1-simple-entity
  • sample2-one-to-one
  • sample3-many-to-one
  • sample4-many-to-many
  • sample16-indexes

Celles-ci traitent des fonctionnalités de base en matière de persistance, de relations et de schémas, et présentent des exemples d'utilisation pratique.

Tests unitaires

Les tests initiaux comprennent les cas d'utilisation suivants :

Modèle d'entité

  • doit enregistrer et utiliser de manière appropriée les méthodes statiques
  • doit recharger correctement l'entité donnée
  • doit recharger exactement la même entité
  • doit effectuer correctement un upsert

Schéma d'entité > Index

  • basique

Persistance

  • fonctionnalité basique
  • mise à jour d'entité
  • insertion > update-relation-columns-after-insertion (mise à jour des colonnes de relation après insertion)
  • plusieurs-à-plusieurs
  • en individuel

Ces tests ont une portée limitée et leur couverture sera élargie au fur et à mesure de l'avancement du projet.

Fonctionnalités prises en charge

  • Décorateurs d'entités: @Entity(), @Column(), @PrimaryGeneratedColumn()
  • Référentiels: create, save, find, delete, etc.
  • Suppression et synchronisation du schéma (expérimental)
  • Prise en charge partielle des relations et des requêtes personnalisées

Attention, ces fonctionnalités sont encore en phase initiale et peuvent ne pas couvrir l'ensemble des capacités de TypeORM.

Limitations réelles

Limitations liées au pilote Node.js d'InterSystems IRIS

  • Une seule connexion utilisable par processus
  • Pas de prise en charge adéquate des opérations parallèles ou du threading
  • Absence de prise en charge native de l'API SQL (via le protocole SQL)
  • Dépendance importante à la communication par messages utilisant un protocole propriétaire

Tant qu'InterSystems n'aura pas mis à jour le pilote officiel afin d'assurer la prise en charge de l'exécution SQL et des opérations simultanées, ce projet restera très limité en termes de performances et d'évolutivité.

Commentaires & Contributions

Comme il s'agit d'un pilote expérimental, vos commentaires sont essentiels. Que vous l'essayiez pour un petit projet parallèle ou que vous l'évaluiez en vue d'une utilisation plus large, n'hésitez pas à partager vos problèmes et suggestions sur GitHub:

➡️ github.com/caretdev/typeorm-iris/issues

Les pull requests, les cas de test et les améliorations de la documentation sont appréciés.

À venir

Les améliorations prévues incluent:

  • Élargissement de la couverture des tests pour les requêtes réelles et les conceptions de schémas
  • Prise en charge d'un plus grand nombre de fonctionnalités du générateur de requêtes TypeORM
  • Analyse des optimisations par lots
  • Amélioration de l'introspection des schémas pour les migrations

Conclusion

typeorm-iris apporte un soutien indispensable de TypeORM à InterSystems IRIS pour les développeurs Node.js. Bien qu'il ne soit pas encore prêt pour la production et qu'il hérite des limitations importantes de l'infrastructure actuelle du pilote, il constitue une base pour poursuivre l'expérimentation et potentiellement élargir son adoption au sein de la communauté des développeurs IRIS.

Si vous êtes un développeur IRIS et cherchez à intégrer un backend Node.js moderne à l'aide de TypeORM, c'est le point de départ idéal.

Et si vous avez trouvé cela utile, n'hésitez pas à voter à pour cette solution dans le concours InterSystems Developer Tools Contest!

0
0 11
Article Iryna Mykhailova · Oct 2, 2025 25m read

Table des matières

  1. Objectif de l'article
  2. Les conteneurs : définition et pertinence dans le cadre d'IRIS
     2.1 Les conteneurs et les images en quelques mots
     2.2 Avantages des conteneurs pour les développeurs
     2.3 Pourquoi IRIS fonctionne bien avec Docker
  3. Conditions préalables
  4. Installation de l'image InterSystems IRIS
     4.1 Utilisation de Docker Hub
     4.2 Extraction de l'image
  5. Exécution de l'image InterSystems IRIS
     5.1 Démarrage d'un conteneur IRIS
     5.2 Vérification de l'état des conteneurs
     5.3 Exécution de code dans le terminal conteneur
     5.4 Accès au portail de gestion IRIS
     5.5 Connexion du conteneur à VS Code
     5.6 Arrêt ou suppression du conteneur
     5.7 Configuration d'un mot de passe spécifique avec un montage de type bind
     5.8 Utilisation de volumes %SYS durables
      5.8.1 Contenu stocké sous %SYS durable
      5.8.2 Comment activer %SYS durable
  6. Utilisation de Docker Compose
     6.1 Exemple d'utilisation de l'outil Docker Compose
     6.2 Exécution de Docker Compose
  7. Utilisation d'un fichier Dockerfile pour l'exécution d'un code source personnalisé
     7.1 Exemple de fichier Dockerfile
     7.2 Exemple de Docker Compose
     7.3 Compréhension des couches, du balisage des images et de la différence entre le temps de compilation et le temps d'exécution
     7.4 Code source et script d'initialisation
     7.5 Création de l'image avec le fichier Dockerfile
     7.6 Exécution d'instructions dans le terminal IRIS conteneurisé
  8. Conclusion et suite


1. Objectif de l'article

La communauté de développeurs InterSystems propose déjà de nombreux articles intéressants qui expliquent le fonctionnement de Docker, les commandes les plus importantes et plusieurs cas d'utilisation d'InterSystems IRIS dans un environnement conteneurisé.

Les articles de cette série ont un objectif quelque peu différent. Je suis un grand fan des guides étape par étape, c'est pourquoi je souhaite créer un guide complet sur la configuration et l'utilisation de Docker avec InterSystems IRIS, en commençant par les bases pour ensuite passer progressivement à des scénarios plus avancés tels que les instances à espaces de noms multiples, les conteneurs interconnectés, les intégrations avec des systèmes externes et les applications comprenant une interface utilisateur.

2. Les conteneurs : définition et pertinence dans le cadre d'IRIS

2.1 Les conteneurs et les images en quelques mots

Traditionnellement, pour exécuter une application, il fallait faire correspondre sa version à celle de votre système d'exploitation et la packager pour cette cible spécifique. Dans le même temps, chaque application doit être conçue pour fonctionner spécifiquement avec un système cible. Si vous vouliez qu'une application fonctionne sous macOS et Windows, vous deviez modifier sa conception et la packager pour ces différents systèmes. Les images et conteneurs Docker constituent des technologies de déploiement d'applications qui résolvent ces problèmes en permettant aux développeurs de packager un logiciel une seule fois et de l'exécuter n'importe où. 

Docker est une plateforme logicielle qui regroupe des logiciels dans des conteneurs. Une image Docker, ou image de conteneur, est un fichier exécutable autonome contenant toutes les instructions (bibliothèques, dépendances et fichiers) nécessaires pour créer et exécuter un conteneur. Une image Docker est partageable et portable, ce qui vous permet de déployer la même image à plusieurs endroits à la fois. Un conteneur Docker est un environnement d'exécution qui contient tous les composants nécessaires pour exécuter le code de l'application sans utiliser les dépendances de la machine hôte.  

Contrairement aux machines virtuelles, les conteneurs sont légers. Ils n'ont pas besoin d'un système d'exploitation complet et fonctionnent directement sur le système d'exploitation hôte via le moteur Docker Engine en utilisant uniquement les binaires et les bibliothèques nécessaires à l'application spécifique, ce qui les rend plus efficaces. Plusieurs conteneurs isolés peuvent être lancés en même temps sur la même machine sans interférer les uns avec les autres.

2.2 Avantages des conteneurs pour les développeurs

  • Isolation: exécution dans un environnement propre et reproductible sans affecter le système hôte.
  • Reproductibilité: assurance que la configuration fonctionne de la même manière sur différentes machines.
  • Configuration facile: lancement d'une instance IRIS en quelques secondes à l'aide d'une seule commande, sans installation manuelle.

2.3 Pourquoi IRIS fonctionne bien avec Docker

L'exécution d'InterSystems IRIS dans Docker présente plusieurs avantages:

  • L'application peut être exécutée et testée dans des conteneurs isolés,
  • Il est possible d'utiliser des systèmes de contrôle de version partagés (tels que Git) au lieu de travailler directement sur le serveur
  • L'environnement conteneurisé peut être répliqué à n'importe quelle étape, ce qui garantit la cohérence tout au long du cycle de vie du logiciel.
  • Votre application peut facilement être exécutée sur n'importe quel ordinateur.

3. Conditions préalables

Pour exécuter InterSystems IRIS dans un conteneur Docker, il faut que vous ayez:

4. Installation de l'image InterSystems IRIS

4.1 Utilisation de Docker Hub

Docker Hub est le registre central des images Docker. Il fournit une vaste bibliothèque d'images pré-construites que vous pouvez utiliser comme point de départ. InterSystems y publie les images officielles IRIS Community Edition que vous pouvez télécharger pour exécuter IRIS localement dans votre conteneur. Vous pouvez également utiliser Docker Hub pour pousser vos propres images personnalisées afin de les partager au sein de votre équipe ou de les distribuer à la communauté. Docker Hub est disponible en ligne et intégré à Docker Desktop.

4.2 Extraction de l'image

Voici quelques commandes courantes pour les images Docker :

CommandeDescription
docker pull <image>Téléchargement d'une image depuis Docker Hub
docker imagesRépertoire de toutes les images locales
docker rmi <image>Suppression d'une ou plusieurs images

Vous trouverez la commande pull exacte directement sur la page Docker Hub de l'image:

Pour l'image InterSystems IRIS, la commande est comme suit:

docker pull intersystems/iris-community:latest-cd

Vous pouvez également rechercher iris-community dans la barre de recherche de Docker Desktop et cliquer sur Pull:

Une fois l'installation terminée, l'image InterSystems IRIS devrait apparaître dans la liste de vos images locales:

5. Exécution de l'image InterSystems IRIS

Une fois l'image InterSystems IRIS extraite de Docker Hub, vous pouvez l'exécuter dans un conteneur. 

Voici les principales commandes pour les conteneurs Docker:

CommandeDescription
docker run -d <image>Exécution en mode détaché : démarrer le conteneur en arrière-plan et renvoyer immédiatement le contrôle au terminal.
docker run -p <host>:<container> <img>Mappage du port hôte vers le port du conteneur
docker psListe des conteneurs en cours d'exécution
docker ps -aListe de tous les conteneurs (y compris les conteneurs arrêtés)
docker exec -it <container> bashExécution d'une commande dans un conteneur en cours d'exécution
docker logs <container>Affichage des journaux d'un conteneur
docker stop <container>Arret d'un conteneur en cours d'exécution
docker start <container>Démarrage d'un conteneur arrêté
docker restart <container>Redémarrage d'un conteneur
docker rm <container>Suppression d'un conteneur

5.1 Démarrage d'un conteneur IRIS

Vous pouvez démarrer un conteneur InterSystems IRIS Community Edition nommé "my-iris" via l'interface utilisateur Docker Desktop en cliquant simplement sur le bouton Run de l'image que vous souhaitez exécuter dans le panneau Images. Pour l'image InterSystems, il est possible de spécifier quelques paramètres facultatifs, tels que les ports à exposer sur votre machine hôte pour communiquer avec les services exécutés dans le conteneur.

Cela peut être fait via le menu ci-dessus, en considérant que:

  • côté gauche → port sur la machine hôte
  • côté droit → port à l'intérieur du conteneur

Ports IRIS courants (à l'intérieur du conteneur)

  • 1972 → Port Superserver : utilisé par IRIS pour les protocoles réseau (ObjectScript, JDBC, etc.).
  • 52773 → Port du serveur Web : utilisé par IRIS pour le portail de gestion (interface Web).

Si vous ne mappez pas explicitement les ports, Docker attribuera des ports aléatoires sur l'hôte.

Vous pouvez également exécuter le conteneur à l'aide du terminal:

docker run --name my-iris -d --publish 9091:1972 --publish 9092:52773 intersystems/iris-community:latest-cd

Dans cet exemple:

  • 9091 sur l'hôte est mappé à 1972à l'intérieur du conteneur (Superserver).
  • 9092 sur l'hôte est mappé à 52773 à l'intérieur du conteneur (Portail de gestion).

5.2 Vérification de l'état des conteneurs

Après avoir exécuté cette commande, exécutez docker ps pour vérifier si le conteneur fonctionne correctement.

> docker ps
CONTAINER ID   IMAGE                                   COMMAND                 CREATED         STATUS                            PORTS                                                                                        NAMES
907d4c2b4ab5   intersystems/iris-community:latest-cd   "/tini -- /iris-main"   3 seconds ago   Up 2 seconds (health: starting)   0.0.0.0:9091->1972/tcp, [::]:9091->1972/tcp, 0.0.0.0:9092->52773/tcp, [::]:9092->52773/tcp   my-iris

Les conteneurs en cours d'exécution sont également répertoriés dans le panneau Containers de Docker Desktop:

L'état des images associées sera "In Use" (En cours d'utilisation), comme indiqué dans le panneau Images :

5.3 Exécution de code dans le terminal conteneur

Une fois que le statut du conteneur indique Running (exécution), tout fonctionne correctement. 

Vous pouvez le tester en ouvrant le terminal du conteneur (cliquez sur le nom du conteneur dans le panneau de conteneurs (Containers) de Docker Desktop et accédez à Exec) et en saisissant:

iris session IRIS

Cela ouvre un terminal IRIS à l'intérieur du conteneur Docker, où vous pouvez utiliser la syntaxe ObjectScript standard pour exécuter des commandes et des scripts.

5.4 Accès au portail de gestion IRIS de l'instance conteneurisée

Ensuite, ouvrez votre navigateur et accédez à:

http://localhost:9092/csp/sys/UtilHome.csp

Par défaut, les conteneurs IRIS se servent de l'utilisateur _SYSTEM avec le mot de passe SYS. Il est nécessaire de changer le mot de passe après la connexion.

Cela vous donne un accès complet au portail de gestion IRISvous permettant de gérer les espaces de noms, les bases de données et d'autres fonctionnalités IRIS directement depuis l'interface web.

5.5 Connexion du conteneur VSCode

Vous pouvez connecter votre IDE préféré (tel que VS Code ou Studio) au conteneur IRIS à l'aide du port Superserver mappé (par défaut 1972 à l'intérieur du conteneur, par exemple, 9091 sur l'hôte) et du port du serveur Web (par défaut 52773 à l'intérieur du conteneur, par exemple, 9092 sur l'hôte). Cela vous permet de développer et de tester du code ObjectScript directement sur le conteneur en cours d'exécution.

Pour connecter le conteneur à VSCode:

  • Installez la solution InterSystems ObjectScript Extension Pack
  • Ouvrez l'extension InterSystems Server
  • Cliquez sur les trois points et sélectionnez "Edit server" (Modifier le serveur)
  •  
  • Cliquez sur "Modifier dans settings.json"
  • Ajoutez cet élément au fichier json "intersystems.servers":
"docker_iris": {
    "webServer": {
         "scheme": "http",
         "host": "localhost",
         "port": 9092
    },
    "description": "Connection to Docker container."
}
  • Maintenant le serveur est connecté. Vous pouvez vous connecter avec l'utilisateur _SYSTEM.
  • Comme vous pouvez le constater, l'espace de noms USER (UTILISATEUR) est le seul disponible:

5.6 Arrêt ou suppression du conteneur

Pour arrêter un conteneur en cours d'exécution, utilisez la commande:

docker stop my-iris

Redémarrage du conteneur

Pour redémarrer le conteneur, utilisez la commande

docker start my-iris

Suppression (effacement) du conteneur

Pour supprimer l'instance du conteneur, mais pas l'image, utilisez la commande: 

docker rm my-iris

La totalité des données contenues dans le conteneur sera perdue, sauf si vous avez monté des volumes (nous en parlerons dans les paragraphes suivants).

5.7 Configuration d'un mot de passe spécifique avec un montage de type bind

Il est possible de configurer un mot de passe personnalisé lors du démarrage d'un conteneur à l'aide d'un fichier de mots de passe. Le fichier sera enregistré sur notre hôte et copié dans le conteneur à l'aide du mécanisme bind mount .

Lorsque vous utilisez un bind mount, un fichier ou un répertoire sur la machine hôte est monté depuis l'hôte dans un conteneur, ce qui permet de partager le code source ou les artefacts de compilation entre un environnement de développement sur l'hôte Docker et un conteneur. 

  • Créez un fichier nommé password.txt contenant uniquement votre mot de passe sous forme de chaîne (attention! Notez votre mot de passe, vous en aurez besoin plus tard). 
  • Copiez le chemin d'accès, dans cet exemple ce sera C:\InterSystems\DockerTest\password\password.txt
  • Exécutez la commande suivante:
docker run --name my-iris -d --publish 9091:1972 --publish 9092:52773 --volume "C:\InterSystems\DockerTest:/durable" intersystems/iris-community:latest-cd --password-file /durable/password/password.txt 

Une fois le conteneur lancé, vous pouvez vous connecter avec l'utilisateur _SYSTEM et le mot de passe que vous avez enregistré dans votre fichier de mots de passe. 

À l'intérieur du conteneur Docker, vous verrez un fichier password.txt.done . Le même fichier se trouvera dans votre dossier hôte.

L'extension du fichier est remplacée par .done afin d'éviter la présence du mot de passe dans le texte brut. Il s'agit du comportement standard d'InterSystems IRIS avec les fichiers de mots de passe. Par conséquent, une fois le mot de passe lu à partir du fichier password.txt et l'utilisateur IRIS par défaut (_SYSTEM) mis à jour avec ce mot de passe, le fichier est mis à jour en ajoutant .done et en supprimant le mot de passe pour des raisons de sécurité.

Vous pouvez vous connecter au Portail de gestion à l'aide de votre mot de passe personnalisé (je vous avais dit de le noter :D). InterSystems IRIS ne vous obligera pas à changer le mot de passe après votre connexion.

Veuillez noter que si le conteneur est supprimé puis redémarré sans aucun volume durable monté (pour plus d'informations, voir le paragraphe suivant), le mot de passe ne sera pas relu à partir du fichier password.txt , car il a été remplacé par le fichier password.txt.done. Dans ce cas, le mot de passe standard "SYS" sera utilisé. 

5.8 Démarrage du conteneur avec un volume durable spécifique. Utilisation de volumes %SYS durables

Par défaut, tout ce que vous enregistrez dans un conteneur Docker en cours d'exécution disparaît lorsque vous le supprimez à l'aide de la commande docker rm <container's name> . 

Pour éviter de perdre vos données, InterSystems IRIS fournit la fonctionnalité durable %SYS. Cette fonctionnalité permet à l'instance de stocker tous les fichiers importants sur votre machine hôte, afin qu'ils survivent aux redémarrages du conteneur et de l'instance.

5.8.1 Contenu stocké sous %SYS durable

Voici quelques exemples:

  • Fichiers de configuration (iris.cpf, httpd.conf)
  • Configurations et journaux de la passerelle Web (/csp)
  • Bases de données système (IRIS, USER, IRISSECURITY, IRISTEMP, etc.)
  • Journaux, fichiers image d'écriture (WIJ) et fichiers temporaires
  • Fichiers journaux (messages.log, SystemMonitor.log, etc.)
  • Clé de licence (iris.key)
  • Toute autre base de données que vous avez créée

5.8.2 Comment activer %SYS durable

La première chose à faire est de choisir un dossier sur votre hôte, tel que C:\InterSystems\DockerTest

  • Sous Linux, assurez-vous qu'IRIS peut y écrire en créant l'utilisateur irisowner et en lui attribuant la propriété du répertoire:
adduser irisowner
chown -R irisowner:irisowner /InterSystems/DockerTest
  • Sous Windows, vous pouvez ignorer cette étape, car Docker Desktop will montera le dossier avec vos autorisations utilisateur Windows actuelles.

Ensuite, montez votre dossier hôte et indiquez à IRIS où écrire ses données durables à l'aide de la variable ISC_DATA_DIRECTORY:

  --volume "C:\InterSystems\DockerTest:/durable" --env ISC_DATA_DIRECTORY=/durable/iris

L'instruction complète à exécuter sur le terminal est la suivante:

docker run --name my-iris -d --publish 9091:1972 --publish 9092:52773 --volume "C:\InterSystems\DockerTest:/durable" --env ISC_DATA_DIRECTORY=/durable/iris intersystems/iris-community:latest-cd --password-file /durable/password/password.txt

À ce stade, vous pouvez inspecter votre conteneur et observer qu'un dossier durable est monté et contient les répertoires iris/ (pour le %SYS durable) et password/. Sur votre hôte, vous pouvez également voir les deux répertoires:

Si le conteneur est arrêté et supprimé, lorsque vous recréez le conteneur avec la même commande de Docker Compose IRIS restaurera son état précédent (utilisateurs, configuration, journaux, bases de données, etc.) à l'aide des données du dossier iris/, de sorte que rien ne soit perdu. Vous pouvez tester cette méthode en créant une application web, en arrêtant et en supprimant le conteneur, puis en le recréant une deuxième fois. Sans utiliser la fonctionnalité durable %SYS, toutes les modifications apportées à %SYS sont perdues lorsque le conteneur est supprimé et l'instance est lancée à chaque fois comme une nouvelle instance. 

Veuillez noter que si vous supprimez le dossier iris/, la prochaine fois que vous lancerez le conteneur IRIS, il sera initialisé comme lors d'une nouvelle installation, car il ne trouvera pas les données %SYS précédentes. Un tout nouveau dossier iris/ sera créé.

6. Utilisation de Docker Compose 

Jusqu'à présent, vous avez démarré InterSystems IRIS à l'aide d'une seule commande longue docker run. Cela fonctionne, mais il devient rapidement difficile de tout gérer avec des commandes shell simples.

Docker Compose est un fichier de configuration YAML qui permet de définir comment exécuter un ou plusieurs conteneurs. Cela simplifie le contrôle de l'ensemble de votre pile d'applications, facilitant la gestion des services, des réseaux et des volumes. Une seule commande vous permet de créer et de démarrer tous les services à partir de votre fichier de configuration.

Voici les commandes courantes pour Docker Compose:

CommandeDescription
docker compose up -dDémarrer tous les services définis dans docker-compose.yml en mode détaché (par défaut, utiliser le fichier docker-compose.yml du dossier courant).
docker compose -f ./path/to/docker-compose.yml up -dDémarrer les services à partir d'un fichier de composition dans un autre répertoire.
docker compose downArrêter et supprimer tous les conteneurs, réseaux et volumes créés par docker compose up.
docker compose psRépertorier les conteneurs gérés par Compose.
docker compose logsAfficher les journaux de tous les services définis dans le fichier Compose.
docker compose logs <service>Afficher les journaux d'un service spécifique (par exemple, iris).
docker compose exec <service> bashOuvrez un shell dans un conteneur en cours d'exécution géré par Compose.
docker compose stopArrêtez les conteneurs en cours d'exécution, mais ne les supprimez pas.
docker compose startDémarrer les conteneurs précédemment arrêtés.
docker compose restartRedémarrer tous les conteneurs définis dans le fichier Compose.
docker compose buildConstruire ou reconstruire les services définis par un fichier Dockerfile.
docker compose pullTélécharger la ou les dernières images pour les services.
docker compose configValider et afficher la configuration fusionnée à partir du fichier Compose.

6.1 Exemple d'utilisation de l'outil Docker Compose

Pour utiliser Docker Compose, il est nécessaire de créer un fichier docker-compose.yml contenant toutes les configurations du conteneur que l'on souhaite créer et démarrer.

Ainsi la commande suivante:

docker run --name my-iris -d --publish 9091:1972 --publish 9092:52773 --volume "C:\InterSystems\DockerTest:/durable" --env ISC_DATA_DIRECTORY=/durable/iris intersystems/iris-community:latest-cd --password-file /durable/password/password.txt

peut être remplacé par la suivante docker-compose.yml:

# docker-compose.yml     
services:
  iris:
    container_name: my-iris
    image: intersystems/iris-community:latest-cd
    init: true
    volumes:
      # Données système/persistantes (installation IRIS, bases de données, etc.)# Sous Windows, vous pouvez utiliser les deux options: "C:\\InterSystems\\DockerTest:/durable"
      - C:/InterSystems/DockerTest:/durable
      ports:
      - "9092:52773"# Portail de gestion / API REST
      - "9091:1972"# Port SuperServer
      environment:
      - ISC_DATA_DIRECTORY=/durable/iris
      # Utilisation du fichier de mots de passe pour se connecter au conteneur
      command: --password-file /durable/password/password.txt

6.2 Exécution de Docker Compose 

Ouvrez un terminal dans le répertoire où le fichier Docker Compose est enregistré (ou utilisez l'option -f) et exécutez la commande suivante:

docker compose up -d

Votre conteneur IRIS démarrera dans la configuration exacte spécifiée dans le fichier Docker Compose

Dans Docker Desktop, vous pouvez désormais voir qu'une pile composée appelée "dockertest" (elle prend le nom du dossier dans lequel Docker Compose est enregistré) a été créée et associée au conteneur "my-iris":

7. Utilisation d'un fichier Dockerfile pour l'exécution d'un code source personnalisé

Jusqu'à présent, vous avez exécuté InterSystems IRIS directement à partir de l'image Docker officielle. Cependant, nous aurons peut-être besoin de télécharger automatiquement des classes ObjectScript ou d'autres codes personnalisés dans l'image lors de sa création. 

Un fichier Dockerfileest un fichier texte contenant des instructions pour créer une image à partir d'une image de base (comme intersystems/iris-community:latest-cd). Grâce à un fichier Dockerfile, nous pouvons ajouter du code source personnalisé dans le conteneur et exécuter des commandes personnalisées lors de la création de l'image. 

7.1 Exemple de fichier Dockerfile

Le prochain exemple fournit un fichier Dockerfile qui effectue les opérations suivantes:

  • Démarrez l' image à partir de l'image officielle InterSystems IRIS.
  • Copiez le code de votre application depuis le dossier src/ vers le conteneur.
  • Exécutez un script pour importer les classes et initialiser l'application conteneurisée, puis enregistrez les journaux dans un fichier journal.
  • Exposez les ports InterSystems IRIS par défaut.

7.2 Exemple d'utilisation de l'outil Docker Compose

Vous devez également modifier Docker Compose afin de spécifier la création de l'image à partir du fichier Dockerfile situé dans le dossier courant:

# docker-compose.yml     
services:
  iris:
    container_name: my-iris
    build: # Cela indique à Docker de créer une nouvelle image basée sur celle spécifiée dans le fichier Dockerfile
      context: .        # Construction de l'image à partir du fichier Dockerfile local
      dockerfile: Dockerfile
    image: my-modified-iris-image:latest   # attribution d'une nouvelle balise à la nouvelle image afin d'éviter de remplacer l'image de base
    init: true
    volumes:
      # Données système/persistantes (installation IRIS, bases de données, etc.)# Sous Windows, vous pouvez utiliser les deux options: "C:\\InterSystems\\DockerTest:/durable"
      - C:/InterSystems/DockerTest:/durable
ports:
      - "9092:52773"# Portail de gestion / API REST
      - "9091:1972"# Port SuperServer
environment:
      - ISC_DATA_DIRECTORY=/durable/iris
    # Utilisation du fichier de mots de passe pour se connecter au conteneur
    command: --password-file /durable/password/password.txt

7.3 Compréhension des couches, du balisage des images et de la différence entre le temps de compilation et le temps d'exécution

Dans Docker, comme expliqué ici: Qu'est-ce qu'une image?, il y a deux principes importants à garder à l'esprit:

  1. Les images sont immuables: une fois qu'une image est créée, elle ne peut plus être modifiée. Vous pouvez uniquement créer une nouvelle image ou y ajouter des modifications.
  2. Les images de conteneur sont composées de couches: chaque couche représente un ensemble de modifications du système de fichiers qui ajoutent, suppriment ou modifient des fichiers.

De nouvelles couches sont ajoutées à une image lorsque les instructions spécifiées dans un fichier Dockerfile sont exécutées. Ces couches apparaîtront également dans les informations relatives à l'image.

Dans cette optique, il est très important de distinguer ce que fait Docker au temps de compilation (lorsqu'il crée une image à partir d'un fichier Dockerfile) et au temps d' exécution (lorsqu'il démarre un conteneur à partir de cette image).

Chacune de ces instructions Dockerfile est exécutée au temps de compilation

  • COPY: les fichiers sont copiés dans l'image
  • RUN: exécute des commandes et enregistre les résultats sous forme de nouvelles couches d'image
  • ENTRYPOINT: ne modifie pas le système de fichiers, mais définit le processus par défaut qui sera lancé au moment de l'exécution
  • EXPOSE: définit les métadonnées relatives aux ports que l'image prévoit d'utiliser

Au temps d'exécution, Docker ne reconstruit pas l'image, mais ajoute une couche de conteneur par-dessus. Toutes les modifications apportées pendant l'exécution du conteneur (comme les nouveaux fichiers, les modifications, les journaux) sont enregistrées dans cette couche temporaire.

D'après le dernier exemple fourni par Docker Compose:

build: # cela indique à Docker de créer une nouvelle image basée sur celle spécifiée dans le fichier Dockerfile
context: .        # Construction de l'image à partir du fichier Dockerfile local
dockerfile: Dockerfile
image: my-modified-iris-image:latest   # attribution d'une nouvelle balise à la nouvelle image afin d'éviter de remplacer l'image de base

Ces instructions indiquent à Docker de créer une nouvelle image appelée "my-modified-iris-image:latest" (ceci s'appelle un tag) en récupérant l'image de base et en la modifiant comme décrit dans le fichier Dockerfile. Il est très important de marquer la nouvelle image avec un nom distinct. Si nous évitons d'apposer une balise sur l'image nouvellement créée, l'image de base sera remplacée par la nouvelle. L'image officielle sera toujours disponible sur Docker Hub, mais cette version locale la masquera et tous les projets faisant référence à cette balise utiliseront désormais, sans le savoir, l'image personnalisée, contenant plusieurs nouvelles couches.

Pour éviter cela, utilisez toujours une balise distincte pour créer une nouvelle image distincte tout en conservant l'image de base officielle propre et réutilisable. 

7.4 Code source et script d'initialisation

À ce stade, nous devons créer au moins une classe et la placer dans le src/ dossier. La classe sera copiée dans le conteneur et importée via le iris.script fichier, qui contient toutes les instructions nécessaires pour importer les classes et initialiser l'application lors de la création de l'image.

Créez un nouveau répertoire appelé src/DockerStepByStep dans le dossier de votre projet et créez le fichier de classe suivant:

Class DockerStepByStep.cheers Extends%RegisteredObject
{

ClassMethod sayHi() As%Status { Set sc = $$$OKw"Hi mom!",! Return sc }

}

Dans la racine de votre projet, créez un fichier portant le nom iris.script:

// Non-expiration des mots de passe pour le mode devzn"%SYS"Do##class(Security.Users).UnExpireUserPasswords("*")

// Téléchargement des cours à partir d'une source fiablezn"USER"// Les classes à importerset importPath = "/opt/irisapp/src"write"Loading classes at '", importPath, "' ...", ! set errors = ""do$System.OBJ.Import(importPath,"cuk",,.errors) if errors = 0 { write"Classes loaded successfully", ! } else { write errors, " errors occurred while loading classes!", ! }

halt

7.5 Création de l'image avec le fichier Dockerfile

Pour la première exécution, vous pouvez utiliser la commande suivante pour créer l'image à partir du fichier Dockerfile (le drapeau --build force Docker à recréer l'image à partir de votre fichier Dockerfile):

docker-compose up --build

Pour les autres exécutions, si le fichier Dockerfile n'a pas été modifié, vous pouvez simplement exécuter ce qui suit: 

docker-compose up -d

Une fois la création de l'image lancée, vous verrez apparaître les journaux suivants dans le terminal:

PS C:\InterSystems\DockerTest> docker-compose up --build
[+] Building 21.5s (13/13) FINISHED
 => [internal] load local bake definitions                                                                     0.0s
 => => reading from stdin 530B                                                                                 0.0s
 => [internal] load build definition from Dockerfile                                                           0.0s
 => => transferring dockerfile: 1.73kB                                                                         0.0s
 => [internal] load metadata for docker.io/intersystems/iris-community:latest-cd                              10.0s
 => [internal] load .dockerignore                                                                              0.0s
 => => transferring context: 2B                                                                                0.0s
 => [1/6] FROM docker.io/intersystems/iris-community:latest-cd@sha256:93488df381f5868649e7bfc33a9083a3e86a22d  0.9s
 => => resolve docker.io/intersystems/iris-community:latest-cd@sha256:93488df381f5868649e7bfc33a9083a3e86a22d  0.0s
 => [internal] load build context                                                                              0.0s
 => => transferring context: 147B                                                                              0.0s
 => [2/6] WORKDIR /opt/irisapp                                                                                 0.0s
 => [3/6] COPY src src                                                                                         0.1s
 => [4/6] COPY iris.script .                                                                                   0.1s
 => [5/6] RUN mkdir -p /opt/irisapp/logs                                                                       0.3s
 => [6/6] RUN iris start IRIS &&     iris session IRIS < iris.script > /opt/irisapp/logs/build.log 2>&1 &&     4.5s
 => exporting to image                                                                                         4.5s
 => => exporting layers                                                                                        3.3s
 => => exporting manifest sha256:3ce316cefa21a3707251c4287005a15b02e6dc0151b24baf2a82f76064792250              0.0s
 => => exporting config sha256:00238e19edef86b29149d2eb89ff75f4d1465ba0d9a2ac4494a14d3bd3746a94                0.0s
 => => exporting attestation manifest sha256:3579cab5c8accc7958090276deb60bd7dbbc2ecbf13af8e7fa8c4ff2dfe91028  0.0s
 => => exporting manifest list sha256:17b969c340f57d611cc7603287cc6db50cffd696258a72b5648ece0a919676ac         0.0s
 => => naming to docker.io/intersystems/iris-community:latest-cd                                               0.0s
 => => unpacking to docker.io/intersystems/iris-community:latest-cd                                            0.9s
 => resolving provenance for metadata file                                                                     0.0s
[+] Running 3/3
 ✔ intersystems/iris-community:latest-cd  Built                                                                0.0s
 ✔ Network dockertest_default             Created                                                              0.1s
 ✔ Container my-iris                      Created                                                              0.2s 

À l'étape 6/6, le fichier iris.script est exécuté dans l'instance IRIS conteneurisée et les journaux sont enregistrés dans le chemin /opt/irisapp/logs/build.log.

Pour afficher les journaux, vous pouvez exécuter l'instruction suivante:

docker exec -it my-iris cat /opt/irisapp/logs/build.log

Vous devriez voir les enregistrements suivants qui vous informent du résultat de la compilation de la classe:

Node: buildkitsandbox, Instance: IRIS

USER>

USER>

%SYS>

%SYS>

%SYS>

%SYS>

USER>

USER>

USER>

USER> Loading classes at '/opt/irisapp/src' ...

USER>

USER>

Load of directory started on 09/16/202507:46:28 Loading file /opt/irisapp/src/DockerStepByStep/cheers.cls as udl

Compilation started on 09/16/202507:46:28 with qualifiers 'cuk' Class DockerStepByStep.cheers is up-to-date. Compilation finished successfully in 0.005s.

Load finished successfully.

USER> Classes loaded successfully

USER>

USER>

Sur Docker Desktop, vous pouvez voir qu'une nouvelle image appelée "my-modified-iris-image" a été créée et s'exécute parallèlement à l'image officielle de base.

Si vous examinez les images, vous constaterez que la version personnalisée est composée de 31 couches, contre 25 couches pour l'originale. Les nouvelles couches correspondent aux instructions exécutées au moment de la compilation par le fichier Dockerfile:

7.6 Exécution d'instructions dans le terminal IRIS conteneurisé

Pour tester les classes, vous pouvez activer le terminal Iris dans l'instance IRIS conteneurisée. Pour ce faire, exécutez l'instruction suivante:

docker exec -it my-iris iris session IRIS

À ce stade, vous pouvez invoquer la méthode de classe à partir de l'espace de noms USER à l'aide de la commande suivante:

do##class(DockerStepByStep.cheers).sayHi()

Finalement, vous devriez obtenir le résultat suivant:

PS C:\InterSystems\DockerTest> docker exec -it my-iris iris session IRIS

Node: 41c3c7a9f2e4, Instance: IRIS

USER>do##class(DockerStepByStep.cheers).sayHi() Hi mom!

8. Conclusion et suite

Nous avons parcouru l'ensemble du cycle de mise en œuvre d'InterSystems IRIS dans Docker:

  • extraction de l'image
  • démarrage et configuration des conteneurs
  • persistance des données avec %SYS durable
  • création d'images personnalisées avec votre propre code et vos propres scripts

Cela suffit pour commencer à expérimenter IRIS dans un environnement conteneurisé et l'utiliser pour le développement.

Un référentiel GitHub contenant tous les fichiers mentionnés dans la dernière partie (Docker Compose, Dockerfile, iris.script, etc.) est disponible.

Ne manquez pas le prochain article!

1
0 24
Article Iryna Mykhailova · Sept 17, 2025 3m read

Salut tout le monde! Ayant récemment rejoint InterSystems, je me suis rendu compte que, même en ayant la version communautaire de la Community Edition totalement gratuite et géniale, la manière d'y accéder n'était pas très claire. J'ai donc décidé de rédiger un guide présentant toutes les différentes façons d'accéder à la version communautaire de la Community Edition d'InterSystems IRIS:

Obtention de la Community Edition d'InterSystems IRIS sous forme de conteneur

L'utilisation d'une instance conteneurisée de la Community Edition est l'approche recommandée pour les personnes qui découvrent le développement sur InterSystems IRIS. À mon avis, c'est aussi la plus simple. La Community Edition d'InterSystems IRIS est disponible sur DockerHub; si vous avez un compte SSO InterSystems, vous pouvez également la trouver dans le registre de conteneurscan also find it in the InterSystems Container Registry.

Quel que soit le cas, vous devrez extraire l'image de votre choix à l'aide de l'interface CLI Docker:

docker pull intersystems/iris-community:latest-em
// or
docker pull containers.intersystems.com/intersystems/iris-community:latest-em

Ensuite, vous devrez démarrer le conteneur: Afin d'interagir avec IRIS en dehors du conteneur (par exemple, pour utiliser le portail de gestion), vous devrez publier certains ports. La commande suivante exécutera le conteneur IRIS Community Edition avec les ports du superserveur et du serveur web publiés ; notez que vous ne pouvez rien avoir d'autre en cours d'exécution qui dépende des ports 1972 ou 52773!

docker run --name iris -d --publish 1972:1972 --publish 52773:52773 intersystems/iris-community:latest-em

Obtention de la Community Edition d'InterSystems IRIS dans le Cloud

Vous souhaitez peut-être éviter complètement le problème d'avoir à gérer une installation locale; et si tel est le cas, vous pouvez vous lancer avec un déploiement cloud de la Community Edition d'InterSystems IRIS. Tous les principaux fournisseurs de cloud sont pris en charge. Consultez notre page Cloud Partners page pour plus d'information. Dans cet exemple, nous allons nous concentrer sur le déploiement sur AWS.

Commencez par rechercher la Community Edition d'InterSystems IRIS sur AWS Marketplace:

Vous pouvez cliquer sur "View purchase options" (Afficher les options d'achat) et vous connecter à votre compte pour afficher la page d'abonnement:

 

En faisant défiler vers le bas, vous pouvez cliquer sur “Subscribe” (s'abonner), remplir l'information requise, puis cliquer sur “Deploy” (déployer) pour déployer la Community Edition d'InterSystems IRIS en tant que nœud cloud! Consultez notre documentation ici déploiement d'InterSystems IRIS dans le cloud pour plus de d'information.

Obtention de la Community Edition d'InterSystems IRIS sous forme de kit d'installation

Si vous préférez travailler avec InterSystems IRIS installé directement sur votre machine et que vous ne souhaitez pas utiliser de conteneurs, vous pouvez télécharger un kit d'installation pour votre système à partir du service InterSystems Evaluation Service. Pour télécharger un kit d'installation, vous devez avoir un identifiant SSO InterSystems. Bonne nouvelle: si vous avez un compte sur la Communauté de développeurs, vous en avez déjà un! Sinon, vous pouvez cliquer sur "Créer un nouveau compte" et suivre les étapes indiquées:

 Une fois connecté, la page suivante devrait s'afficher. Cliquez sur "Télécharger la version Community Edition" pour commencer le téléchargement du kit d'installation:

 

Vous serez invité à fournir certaines information concernant la version exacte d'InterSystems IRIS dont vous avez besoin et la plate-forme sur laquelle vous allez l'installer:

 

Dans la plupart des cas, vous devrez sélectionner « InterSystems IRIS Community » et la version la plus récente disponible pour votre plateforme. Suivez les instructions d'installation fournies dans la documentation spécifique à votre plateforme, et le tour est joué!

0
0 25
Article Iryna Mykhailova · Août 28, 2025 12m read

Nous présentons ici le processus d'utilisation de la célèbre solution Jaeger pour tracer les applications InterSystems IRIS. Jaeger est un produit open source permettant de suivre et d'identifier des problèmes, en particulier dans les environnements distribués et de microservices. Ce backend de traçage, apparu chez Uber en 2015, a été inspiré par Dapper de Google et OpenZipkin de Twitter. Il a ensuite rejoint la Fondation Cloud Native Computing (CNCF) en tant que projet en incubation en 2017, avant d'obtenir le statut gradué en 2019. Ce guide vous montrera comment utiliser la solution Jaeger conteneurisée intégrée à IRIS.

Caractéristiques du Jaeger

  1. Surveillance des flux de transactions exécutés par une ou plusieurs applications et composants (services) dans des environnements conventionnels ou distribués:
  2. Identification précise des goulots d'étranglement dans les flux métier, y compris ceux qui sont distribués:
  3. Étude et optimisation des dépendances entre services, composants, classes et méthodes:
  4. Identification des indicateurs de performance pour découvrir les possibilités d'amélioration:

Composants Jaeger



Extrait de la documentation Jaeger: https://www.jaegertracing.io/docs/1.23/architecture/

  1. Client: Solutions, applications ou technologies (telles que IRIS) qui envoient des données de surveillance à Jaeger.
  2. Agent: Démon réseau qui surveille les segments envoyés via UDP, les regroupe et les transmet au collecteur. Il est conçu pour être déployé sur tous les hôtes en tant que composant d'infrastructure, en abstraisant le routage et la découverte des collecteurs depuis le client.
  3. Collecteur: Récepteur des traces provenant des agents Jaeger, qui les traite via un pipeline de traitement , lequel valide les traces, les indexe, effectue les transformations nécessaires, puis les stocke.
  4. Agent d'ingestion (facultatif): Si une file d'attente de messages telle qu'Apache Kafka tamponne les données entre le collecteur et le stockage, l'agent d'ingestion Jaeger lit les données dans Kafka et les écrit dans le stockage.
  5. Requête: Service qui récupère les traces du stockage et héberge une interface utilisateur pour les afficher.
  6. Interface utilisateur: L'interface Web Jaeger pour analyser et tracer les flux de transactions.

Aperçu d'Open Telemetry (OTel)

Étant donné qu'OpenTelemetry est la technologie exploitée par IRIS pour envoyer des données de traçage à Jaeger, il est important de comprendre son fonctionnement.
OpenTelemetry (alias OTel) est un framework d'observabilité open source et fournisseur neutre permettant d'instrumenter, de générer, de collecter et d'exporter des données de télémétrie telles que des traces, des métriques et des logs. Dans cet article, nous nous concentrerons sur la fonctionnalité traces.
L'unité fondamentale de données dans OpenTelemetry est le “signal”. L'objectif d'OpenTelemetry est de collecter, traiter et exporter ces signaux, qui sont des sorties système décrivant l'activité sous-jacente du système d'exploitation et des applications s'exécutant sur une plateforme. Un signal peut être quelque chose que vous souhaitez mesurer à un moment précis (par exemple, la température, l'utilisation de la mémoire) ou un événement qui traverse les composants de votre système distribué que vous souhaitez tracer. Vous pouvez regrouper différents signaux afin d'observer le fonctionnement interne d'une même technologie sous plusieurs angles (source: https://opentelemetry.io/docs/concepts/signals/). Cet article explique la manière d'émettre des signaux associés à des traces (le cheminement d'une requête dans votre application) depuis IRIS vers les collecteurs OTel.
OpenTelemetry est un excellent choix pour surveiller et tracer votre environnement IRIS et votre code source, car il est pris en charge par plus de 40 fournisseurs d'observabilité. Il est également intégré à de nombreuses bibliothèques, services et applications, et adopté par de nombreux utilisateurs finaux (source: https://opentelemetry.io/docs/).

  • Les microservices développés en Java, .NET, Python, NodeJS, InterSystems ObjectScript et des dizaines d'autres langages peuvent envoyer des données de télémétrie à un collecteur OTel à l'aide des points de terminaison et des ports distants (dans notre exemple, nous utiliserons un port HTTP).
  • Les composants de l'infrastructure peuvent également envoyer des données, notamment des données sur les performances, l'utilisation des ressources (processeur, mémoire, etc.) et d'autres informations pertinentes pour la surveillance de ces composants. Pour InterSystems IRIS, les données sont collectées par Prometheus à partir du point de terminaison /monitor et peuvent être transférées vers un collecteur OTel.
  • Les API et les outils de base de données peuvent également envoyer des données de télémétrie. Certains produits de base de données sont capables de le faire automatiquement (instrumentalisation).
  • Le collecteur OTel reçoit les données OTel et les stocke dans une base de données compatible et/ou les transmet à des outils de surveillance (par exemple, Jaeger).


Comment InterSystems IRIS envoie des données de surveillance à Jaeger

À partir de la version 2025, InterSystems a lancé la prise en charge d'OpenTelemetry (OTel) dans son API de surveillance. Cette nouvelle fonctionnalité inclut l'émission de données de télémétrie pour le traçage, la journalisation et les métriques d'environnement. Cet article traite de l'envoi de données de traçage à Jaeger via OTel. Vous trouverez plus de détails ici: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AOTEL&ADJUST=1.
Bien que certains langages de programmation et certaines technologies prennent en charge la transmission automatique des données OTel (instrumentation automatique), pour IRIS, vous devez écrire des instructions de code spécifiques pour activer cette fonctionnalité. Téléchargez le code source de l'application exemple à partir de https://openexchange.intersystems.com/package/iris-telemetry-sample, ouvrez votre IDE et suivez les étapes ci-dessous:

1. Accédez à la classe dc.Sample.REST.TelemetryUtil. La méthode SetTracerProvider initialise un fournisseur de traceurs, ce qui vous permet de définir le nom et la version du service surveillé:

/// Définition du fournisseur de traceursClassMethod SetTracerProvider(ServiceName As%String, Version As%String) As%Status
{
    Set sc = $$$OKset attributes("service.name") = ServiceName
    set attributes("service.version") = Version
    Set tracerProv = ##class(%Trace.TracerProvider).%New(.attributes)
    Set sc = ##class(%Trace.Provider).SetTracerProvider(tracerProv)
    Quit sc
}

2. L'étape suivante consiste à récupérer l'instance du fournisseur de traceurs créée:
 

/// Obtention du fournisseur de traceursClassMethod GetTracerProvider() As%Trace.Provider
{
    Return##class(%Trace.Provider).GetTracerProvider()
}

3. Cet exemple va surveiller l'API PersonREST sur le point de terminaison /persons/all. Accédez à la classe dc.Sample.PersonREST (méthode de classe GetAllPersons):

/// Récupération de tous les enregistrements de dc.Sample.PersonClassMethod GetAllPersons() As%Status
{
<span class="hljs-keyword">#dim</span> tSC <span class="hljs-keyword">As</span> <span class="hljs-built_in">%Status</span> = <span class="hljs-built_in">$$$OK</span>
<span class="hljs-keyword">do</span> <span class="hljs-keyword">##class</span>(dc.Sample.REST.TelemetryUtil).SetTracerProvider(<span class="hljs-string">"Get.All.Persons"</span>, <span class="hljs-string">"1.0"</span>)
<span class="hljs-keyword">set</span> tracerProv = <span class="hljs-keyword">##class</span>(dc.Sample.REST.TelemetryUtil).GetTracerProvider()
<span class="hljs-keyword">set</span> tracer = tracerProv.GetTracer(<span class="hljs-string">"Get.All.Persons"</span>, <span class="hljs-string">"1.0"</span>)

4. Le fournisseur de traceurs vient de créer un service de surveillance appelé Get.All.Persons (version 1.0) et a obtenu l'instance de traceurs avec GetTracer.
5. L'exemple doit créer un racine Span comme suit:
 

set rootAttr("GetAllPersons") = 1set rootSpan = tracer.StartSpan("GetAllPersons", , "Server", .rootAttr)
    set rootScope = tracer.SetActiveSpan(rootSpan)

6. Les spans sont des éléments du flux de traçage. Chaque span doit être mappé à un élément du code source que vous souhaitez analyser.
7. SetActiveSpan est obligatoire pour définir le span actuel à surveiller.
8. À présent, l'exemple crée plusieurs spans descendants mappés à des éléments importants du flux:
 

set childSpan1 = tracer.StartSpan("Query.All.Persons")
    set child1Scope = tracer.SetActiveSpan(childSpan1)
    Try {
        Set rset = ##class(dc.Sample.Person).ExtentFunc()
        do childSpan1.SetStatus("Ok")
    } Catch Ex {
        do childSpan1.SetStatus("Error")
    }
    do childSpan1.End()
    kill childSpan1

9. Ce premier span des descendant surveille la requête pour toutes les personnes dans la base de données. Pour créer un span descendant, l'exemple utilise StartSpan avec un titre suggéré (Query.All.Persons). Le span actif doit ensuite être défini sur le span descendant actuel, ce que l'exemple réalise à l'aide de SetActiveSpan avec la référence childSpan1.
10. L'exemple exécute le code source métier (Set rset = ##class(dc.Sample.Person).ExtentFunc()). Si l'opération réussit, il définit le statut sur "Ok" ; sinon, il le définit sur "Error."
11. L'exemple termine la surveillance de ce morceau de code à l'aide de la méthode End et en supprimant la référence childSpan1.
12. Vous pouvez répéter cette procédure pour tous les autres segments de code que vous souhaitez examiner:

Pour surveiller la détection d'une personne par son ID (obtenir les détails de la personne):

set childSpan2 = tracer.StartSpan("Get.PersonByID")
set child2Scope = tracer.SetActiveSpan(childSpan2)
Set person = ##class(dc.Sample.Person).%OpenId(rset.ID)

       Pour observer le calcul de l'âge (classe dc.Sample.Person, méthode CalculateAge):
 

set tracerProv = ##class(dc.Sample.REST.TelemetryUtil).GetTracerProvider()
set tracer = tracerProv.GetTracer("Get.All.Persons", "1.0")
set childSpan1 = tracer.StartSpan("CalculateAge")
set child1Scope = tracer.SetActiveSpan(childSpan1)

Pour vérifier la définition du signe du zodiaque (classe dc.Sample.Person, méthode CalculateZodiacSign):
 

set tracerProv = ##class(dc.Sample.REST.TelemetryUtil).GetTracerProvider()
set tracer = tracerProv.GetTracer("Get.All.Persons", "1.0")
set childSpan1 = tracer.StartSpan("GetZodiacSign")
set child1Scope = tracer.SetActiveSpan(childSpan1)

 
Configuration des conteneurs IRIS et Jaeger

1. Créez le conteneur collecteur OTel (dans docker-composer.yml):
 

# --- 2. Collecteur OpenTelemetry ---  otel-collector:    image:otel/opentelemetry-collector-contrib:latest    command:["--config=/etc/otel-collector-config.yml"]    volumes:      -./otel-collector-config.yml:/etc/otel-collector-config.yml    ports:      -"4317:4317"# OTLP gRPC       -"4318:4318"# OTLP HTTP      -"9464:9464"# Métriques    depends_on:      -iris      -jaeger

2. Ajustez le fichier de configuration du collecteur OTel:

receivers:  otlp:    protocols:      grpc:        endpoint:"0.0.0.0:4317"      http:        endpoint:"0.0.0.0:4318"exporters:  otlp:    endpoint:jaeger:4317# Le nom du service « jaeger » de docker-compose pour le collecteur gRPC    tls:      insecure:true  prometheus:    endpoint:"0.0.0.0:9464"  debug:{}processors:  batch:# Processeur pour regrouper des traces en lots    send_batch_size:100    timeout:10sconnectors:  spanmetrics:# Connecteur SpanMetricsservice:  pipelines:    traces:      receivers:[otlp]      processors:[batch]# Les traces sont traitées pour générer des métriques      exporters:[otlp,spanmetrics]    metrics:      receivers:[otlp,spanmetrics]      exporters:[prometheus]    logs:      receivers:[otlp]      exporters:[debug]

3. Le collecteur OTel recevra les données de surveillance à l'adresse suivante:

http:        endpoint:"0.0.0.0:4318"

4. Le collecteur OTel enverra les exportateurs (Exporters) à Jaeger comme indiqué ci-dessous:

exporters:
  otlp:
    endpoint: jaeger:4317 # O nome do serviço 'jaeger' do docker-compose para o Collector gRPC
    tls:
      insecure: true

5. Le service OTel créera un pipeline pour recevoir et envoyer les données de surveillance:
 

service:  pipelines:    traces:      receivers:[otlp]      processors:[batch]      exporters:[otlp]

6. Dans le fichier docker-compose.yml, l'exemple va créer un conteneur IRIS, en définissant OTEL_EXPORTER_OTLP_ENDPOINT avec l'adresse du collecteur OTel:

iris:    build:      context:.      dockerfile:Dockerfile    restart:always    ports:      -51773:1972      -52773:52773      -53773    volumes:      -./:/home/irisowner/dev    environment:    -ISC_DATA_DIRECTORY=/home/irisowner/dev/durable    -OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318

 
7. Enfin, un conteneur Jaeger est créé pour recevoir les données du collecteur OTel et fournir une interface utilisateur permettant aux utilisateurs de surveiller et de tracer les données IRIS OTel:

jaeger:    image:jaegertracing/all-in-one:latest    ports:      -"16686:16686"# Jaeger UI      -"14269:14269"# Jaeger Metrics      -"14250:14250"# Jaeger Collector gRPC     environment:      -COLLECTOR_OTLP_ENABLED=true

 
Il existe des options de port supplémentaires pour Jaeger qui vous permettent de traiter plusieurs types de collecteurs. Vous trouverez ci-dessous la liste des ports exposés disponibles:

  • 6831/udp: Accepte les spans jaeger.thrift (Thrift compact)
  • 6832/udp: Accepte les spans jaeger.thrift (Thrift binaire)
  • 5778: Configuration Jaeger
  • 16686: Interface utilisateur Jaeger
  • 4317: Récepteur gRPC du protocole OpenTelemetry (OTLP)
  • 4318: Récepteur HTTP du protocole OpenTelemetry (OTLP)
  • 14250: Acceptation des spans model.proto via gRPC
  • 14268: Acceptation directe des spans jaeger.thrift via HTTP
  • 14269: Vérification de l'intégrité de Jaeger
  • 9411: Compatibilité Zipkin

Exécution de l'exemple

1. Copiez le code source de l'exemple: 

$ git clone https://github.com/yurimarx/iris-telemetry-sample.git

2. Ouvrez le terminal dans ce répertoire et exécutez le code ci-dessous:

$ docker-compose up -d --build

3. Créez quelques données de test simulées. Pour ce faire, ouvrez le terminal IRIS ou le terminal web sur /localhost:52773/terminal/ and call the following:

USER>do##class(dc.Sample.Person).AddTestData(10)

4. Ouvrez http://localhost:52773/swagger-ui/index.html et exécutez le point de terminaison /persons/all.

 

Analyse et traçage du code IRIS sur Jaeger

1. Accédez à Jaeger ici http://localhost:16686/search.

2. Sélectionnez Get.All.Persons dans le champ "Service", puis cliquez sur "Find Traces" (Rechercher les traces).


3. Cliquez sur la trace détectée pour afficher ses détails.


4. Observez la chronologie de la trace sélectionnée.


5. Dans le coin supérieur droit, sélectionnez "Trace Graph" :


6. Analysez les dépendances:


7. Cette analyse des dépendances est essentielle pour identifier les problèmes dans les systèmes/services distants au sein de votre environnement, en particulier si différents services distribués utilisent le même nom de service.
8. Maintenant, sélectionnez “Framegraph”:


9. Observez tous les composants du flux de transactions surveillé dans une table graphique:


10. Grâce à toutes ces ressources Jaeger, vous pouvez résoudre efficacement les problèmes de performances et identifier la source des erreurs.

 

Pour en savoir plus

Pour approfondir vos connaissances sur le traçage distribué, consultez les ressources externes suivantes:

  • Maîtrise du traçage distribué (Mastering Distributed Tracing) (2019) par Yuri Shkuro : article de blog rédigé par le créateur de Jaeger, qui explique l'histoire et les choix architecturaux à l'origine de Jaeger. Ce livre traite en détail de la conception et du fonctionnement de Jaeger, ainsi que du traçage distribué en général.
  • Suivez Jaeger pour un tour en HotROD: un tutoriel étape par étape qui montre comment utiliser Jaeger pour résoudre les problèmes de performances des applications.
  • Présentation de Jaeger : un (ancien) webinaire présentant Jaeger et ses capacités.
  • Tutoriel détaillé sur Jaeger: https://betterstack.com/community/guides/observability/jaeger-guide/ 
  • Évolution du traçage distribué au sein d'Uber.
  • Émettre des données de télémétrie vers un outil de surveillance compatible avec OpenTelemetry par la documentation InterSystems.
  • Observabilité moderne avec InterSystems IRIS & et OpenTelemetry: Une vidéo de démonstration sur la manière de travailler avec OpenTelemetry et IRIS: https://www.youtube.com/watch?v=NxA4nBe31nA
  • OpenTelemetry sur GitHub : Une collection d'API, de SDK et d'outils pour instrumenter, générer, collecter et exporter des données de télémétrie (métriques, journaux et traces) afin d'aider à analyser les performances et le comportement de logiciels: https://github.com/open-telemetry.
0
0 21
Article Iryna Mykhailova · Août 18, 2025 4m read

Récompense d’août pour les articles sur Global Masters a retenu mon attention, et l'un des sujets proposés m'a semblé très intéressant quant à son utilisation future dans mon enseignement. Voici donc ce que j'aimerais dire à mes étudiants à propos des tables dans IRIS et de leur corrélation avec le modèle objet.

Tout d'abord, InterSystems IRIS dispose d'un modèle de données unifié. Cela signifie que lorsque vous travaillez avec des données, vous n'êtes pas enfermé dans un paradigme unique. Les mêmes données sont accessibles et manipulables comme une table SQL traditionnelle, comme un objet natif, ou même comme un tableau multidimensionnel (global). Cela signifie que lorsque vous créez une table SQL, IRIS crée automatiquement une classe d'objet correspondante. Lorsque vous définissez une classe d'objet, IRIS la rend automatiquement disponible sous forme de table SQL. Les données elles-mêmes ne sont stockées qu'une seule fois dans le moteur de stockage multidimensionnel performant d'IRIS. Le moteur SQL et le moteur objet sont simplement des « optiques » différentes pour visualiser et travailler avec les mêmes données.

Commençons par examiner la corrélation entre le modèle relationnel et le modèle objet :

Relationnel Objet
Table Classe
Colonne Propriété
Ligne Objet
Clé primaire Identifiant d'objet

La corrélation n'est pas toujours exacte, car plusieurs tables peuvent représenter une même classe, par exemple. Mais c'est une règle générale.

0
0 23
Article Iryna Mykhailova · Août 13, 2025 8m read

Au fil des ans, j'ai constaté que certaines questions SQL revenaient régulièrement au sein de la Communauté des développeurs InterSystems, notamment concernant l'utilisation du prédicat LIKE dans différents contextes. Parmi les variantes courantes, on peut citer :

et bien d'autres dérivés. J'ai donc décidé d'écrire un article consacré au fonctionnement de LIKE dans InterSystems IRIS SQL, notamment lorsqu'il est utilisé avec des variables dans Embedded SQL, Dynamic SQL et les requêtes de classes, tout en abordant l'échappement de motifs et les recherches de caractères spéciaux.

Tout d'abord, je tiens à préciser qu'InterSystems IRIS SQL offre la plupart des fonctionnalités disponibles dans d'autres bases de données relationnelles implémentant une version ultérieure de la norme SQL. Il est toutefois important de préciser qu'outre l'accès relationnel, IRIS permet également d'utiliser d'autres modèles pour obtenir les mêmes données, par exemple des modèles objet ou document.

À ce propos, examinons le prédicat LIKE et son utilisation en SQL pour la recherche de motifs.

0
0 33
Article Iryna Mykhailova · Août 7, 2025 9m read

Au cours des 10 à 15 dernières années, vous avez sûrement entendu parler des termes Data Lake, Data Warehouse, et Data Fabric à tout bout de champ. Tout peut être réglé et résolu grâce à l'un de ces trois concepts ou à une leur combinaison ( ici et ici vous trouverez deux articles de notre site officiel si vous avez des doutes sur la signification de chacun de ces termes). Si nous devions résumer visuellement l'objectif de tous ces termes, nous pourrions dire qu'ils visent tous à résoudre des situations comme celle-ci:

0
0 9
Article Iryna Mykhailova · Juil 4, 2025 3m read

Une qualité de service (QoS) est attribuée à tous les pods. Trois niveaux de priorité sont attribués aux pods d'un nœud. Ces niveaux sont les suivants :

1) Garanti : Priorité élevée

2) Évolutif : Priorité moyenne

3) Meilleur effort : Priorité faible

Il s'agit d'indiquer au kubelet quelles sont vos priorités sur un nœud donné si des ressources doivent être récupérées. Ce superbe GIF d'Anvesh Muppeda ci-dessous l'explique.

Si des ressources doivent être libérées, les pods avec une QoS « Meilleur effort » seront d'abord évincés, puis ceux avec une QoS « Évolutif », et enfin ceux avec une QoS garantie. L'idée est qu'en évinçant les pods de priorité inférieure, nous récupérerons suffisamment de ressources sur le nœud pour éviter d'avoir à évincer les pods avec une QoS garantie.

0
0 30
Article Iryna Mykhailova · Juin 24, 2025 3m read

J'écris cet article principalement pour recueillir un consensus informel sur la façon dont les développeurs utilisent Python avec IRIS. N'hésitez donc pas à répondre au sondage à la fin de cet article ! Dans le corps de l'article, je détaillerai chaque choix proposé, ainsi que ses avantages, mais n'hésitez pas à le parcourir et à simplement répondre au sondage.

0
0 36
Article Iryna Mykhailova · Juin 17, 2025 5m read

Lorsque nous créons un référentiel FHIR dans IRIS, nous avons un point de terminaison pour accéder à l'information, créer de nouvelles ressources, etc. Mais il y a certaines ressources dans FHIR que nous n'aurons probablement pas dans notre référentiel, par exemple, la ressource Binary (cette ressource renvoie des documents, comme des PDF par exemple).

J'ai créé un exemple dans lequel, quand une ressource est demandée via "Binary", l'endpoint FHIR renvoie une réponse, comme si elle existait dans le référentiel. 

Tout d'abord, nous avons besoin du Namespace (espace de noms) et du FHIR endpoint (point d'accès FHIR). Ensuite, nous devons configurer une production d'interopérabilité - Interoperability production - qui sera connectée à l'endpoint FHIR. Cette production devrait contenir les éléments suivants:

  • Business Operation:
    • HS.Util.Trace.Operations (Ceci est évidemment facultatif, mais peut s'avérer très utile)
    • HS.FHIRServer.Interop.Operation, avec la propriété TraceOperations configurée à *FULL*
  • Business Service:
    • HS.FHIRServer.Interop.Service, avec la propriété TraceOperations configurée à *FULL* et Target Config Name configurée à  HS.FHIRServer.Interop.Operation name

La production se présente comme suit:

Après avoir créé cette production, nous devons la connecter avec l'endpoint FHIR. Il faut donc éditer l'endpoint FHIR et configurer le paramètre Service Config Name avec le nom du Business Service:

Maintenant, si nous commençons à envoyer des requêtes au référentiel FHIR, nous verrons toutes les traces dans l'Afficheur de messages:

Nous pouvons maintenant établir un Business Process pour contrôler ce qu'il convient de faire avec des chemins d'accès spécifiques.

Dans cet exemple, nous avons un Business Process qui reçoit toutes les demandes (maintenant le Business Service est connecté à ce Business Process, au lieu du Business Operation) et 2 nouvelles Business Operations qui effectuent d'autres actions dont nous parlerons plus tard:

Jetons un coup d'œil au Business Process appelé FHIRRouter:

En regardant, nous verrons que, si RequestPath contient "Binary/", alors nous ferons quelque chose avec cette requête: générer notre réponse Binary personnalisée. Sinon, nous enverrons la demande au référentiel FHIR directement.

Jetons un coup d'œil à la séquence appelée "Generate Binary":

Tout d'abord, nous créons une nouvelle instance de HS.FHIRServer.Interop.Response. Nous obtenons document ID à partir du chemin d'accès: Request Path. Comment? Chaque fois que quelqu'un veut une ressource binaire, elle doit être demandée avec l'identifiant du document dans le chemin de l'URL, quelque chose comme ceci: ..../fhir/r4/Binary/XXXXX. Ainsi, pour extraire l'identifiant du document du chemin d'accès, nous utilisons l'expression suivante:

$Replace(request.Request.RequestPath,"Binary/","")

(Ce n'est pas vraiment raffiné, mais cela fonctionne).

Si nous avons un identifiant de document, nous faisons appel à un Business Operation appelée Find pour rechercher le nom de fichier associé à cet identifiant de document:

En fait, cette recherche deBusiness OperationFind renvoie toujours le même nom de fichier:

L'exemple ci-dessus montre ce que nous pouvons faire.

Si nous avons un nom de fichier, alors, nous appelons un autre Business Operation appelé File pour obtenir le contenu de ce fichier, encodé en base64:

And finally, we can return 2 kind of responses:

  • Si nous n'avons pas de contenu de fichier (puisque nous n 'avons pas d 'identifiant de document ou que nous ne trouvons pas le nom de fichier ou le contenu associé), nous renvoyons une réponse 404, avec cette réponse personnalisée:
 set json = {
    "resourceType": "OperationOutcome",
    "issue": [
        {
            "severity": "error",
            "code": "not-found",
            "diagnostics": "<HSFHIRErr>ResourceNotFound",
            "details": {
                "text": "No resource with type 'Binary'"
            }
        }
    ]
 }
 set json.issue.%Get(0).details.text = json.issue.%Get(0).details.text_" and id '"_context.docId_"'"
 set qs = ##class(HS.SDA3.QuickStream).%New()
 do qs.Write(json.%ToJSON())
 set response.QuickStreamId = qs.%Id()
 set response.ContentType = "application/fhir+json"
 set response.CharSet = "UTF-8"
  • Si nous avons le contenu du fichier, nous renvoyons une réponse 200 avec cette réponse personnalisée:
 set json = {
  "resourceType": "Binary",
  "id": "",
  "contentType": "application/pdf",
  "securityContext": {
    "reference": "DocumentReference/"
  },
  "data": ""
 }
 set json.id = context.docId
 set json.securityContext.reference = json.securityContext.reference_json.id
 set json.data = context.content.Read(context.content.Size)
 
 set qs = ##class(HS.SDA3.QuickStream).%New()
 do qs.Write(json.%ToJSON())
 set response.QuickStreamId = qs.%Id()
 set response.ContentType = "application/fhir+json"
 set response.CharSet = "UTF-8"

La clé ici est de créer un HS.SDA3.QuickStream, qui contient l'objet JSON. Et d'ajouter ce QuickStream à la réponse.

Et maintenant, si nous testons notre point d'accès, si nous demandons un document de type Binary, nous verrons la réponse:

Et si nous requérons un document de type Binary qui n'existe pas (vous pouvez le tester sans spécifier l'identifiant du document), nous obtiendrons la réponse 404:

En résumé, en connectant notre endpoint FHIR avec l'interopérabilité, nous pouvons faire tout ce que nous voulons, avec toutes les capacités d'InterSystems IRIS.

0
0 24
Article Iryna Mykhailova · Mai 29, 2025 8m read

J'ai un nouveau projet qui consiste à stocker des informations provenant de réponses REST dans une base de données IRIS. Il me faudra synchroniser les informations provenant d'au moins deux douzaines de points de terminaison REST distincts, ce qui implique la création de presque autant de classes ObjectScript pour stocker les résultats de ces points de terminaison.

Puis-je utiliser ChatGPT pour créer ces classes? La réponse est "Oui", ce qui est génial puisqu'il s'agit de ma première tentative d'utiliser l'IA générative pour quelque chose d'utile. En effet, générer des images de girafes mangeant de la soupe devenait un peu vieux….

Voici ce que j'ai fait:

  • Obtenez un exemple de sortie d'appel REST JSON en utilisant "curl" pour interroger le point de terminaison.
  • Visitez ChatGPT (InterSystems fournit une licence à l'échelle de l'entreprise pour les employés)
  • Accédez au dialogue suivant avec ChatGPT.

J'ai placé la demande suivante dans la fenêtre de message de ChatGPT:

Moi: Générer une classe ObjectScript qui contiendra les données du JSON suivant:

J'ai appuyé sur "Return" avant d'entrer le JSON et ChatGPT m'a donc dirigé comme suit:

ChatGPT: Veuillez fournir la structure JSON pour laquelle vous souhaitez que je génère la classe ObjectScript. Si vous avez un exemple d'objet JSON, collez-le ici et je créerai pour vous la classe ObjectScript InterSystems correspondante.

J'ai saisi ce qui suit dans la fenêtre de message:

{
  "id": 335818632,
  "appId": 113,
  "appName": "Batch",
  "contentId": 182455380233,
  "subject": "Test Subject",
  "name": "Test email campaign name",
  "counters": {
    "processed": 3910,
    "deferred": 86,
    "unsubscribed": 17,
    "statuschange": 17,
    "bounce": 153,
    "mta_dropped": 8,
    "dropped": 299,
    "delivered": 3749,
    "sent": 3903,
    "click": 16,
    "open": 323
  },
  "lastProcessingFinishedAt": 1733232499000,
  "lastProcessingStartedAt": 1733232303000,
  "lastProcessingStateChangeAt": 1733232499000,
  "numIncluded": 4201,
  "processingState": "DONE",
  "scheduledAt": 1733232300000,
  "type": "BATCH_EMAIL"
}

Réponse de ChatGPT:

Voici la classe ObjectScript InterSystems qui correspond à la structure JSON fournie:

Class MyNamespace.MyJSONData Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyNamespace.Counters;Property lastProcessingFinishedAt As%BigInt;Property lastProcessingStartedAt As%BigInt;Property lastProcessingStateChangeAt As%BigInt;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%BigInt;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyNamespace.MyJSONData) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyNamespace.Counters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statuschange As%Integer;Property bounce As%Integer;Property mta_dropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

Avec une explication:

Ce code est un bon départ, mais j'aimerais qu'il s'intègre dans mon paquetage nommé “MyPkg” et que la classe porte le nom “EmaiCampaign”. De plus, grâce à mes connaissances en NI, je sais que les valeurs qui ont généré les propriétés “%BigInt” sont en fait des horaires Linux et que les propriétés devraient être %Library.PosixTime”.

Je fais la demande suivante (je ne sais pas trop pourquoi je dis "s'il vous plaît" à une machine;  je suppose que mes parents ont fait du bon travail):

Moi: Pouvons-nous recréer la classe, s'il vous plaît, en renommant le paquetage " MyPkg" et la classe "EmailCampaign"? De plus, veuillez changer les types de propriétés de %BigInt à %Library.PosixTime.

Reponse de ChatGPT:

Voici la classe ObjectScript mise à jour avec le nom de paquetage et le nom de classe spécifiés, et les propriétés d'horodatage mises à jour pour l'utilisation de %Library.PosixTime:

Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyPkg.Counters;Property lastProcessingFinishedAt As%Library.PosixTime;Property lastProcessingStartedAt As%Library.PosixTime;Property lastProcessingStateChangeAt As%Library.PosixTime;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%Library.PosixTime;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyPkg.EmailCampaign) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyPkg.Counters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statuschange As%Integer;Property bounce As%Integer;Property mta_dropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

Avec une explication suivante:

Après avoir copié le code "tel quel" dans VSCode, des problèmes de syntaxe avec les accolades ont été mis en évidence. Les classes ObjectScript ne permettent pas de définir des classes internes.

Demander à ChatGPT de résoudre le problème des « accolades » en faisant de la classe interne MyPkg.Counters une classe séparée est une opération impossible “no-op”; un humain devra déplacer cette classe dans un autre fichier. ChatGPT a renommé cette classe après la demande suivante:

Moi: La classe telle qu'elle est écrite ne compile pas. Pouvons-nous déplacer la classe interne nommée MyPkg.Counters de la classe principale vers sa propre classe et la renommer en MyPkg.CampaignCounters?

Reponse de ChatGPT:

Voici le code ObjectScript mis à jour avec la classe MyPkg.CampaignCounters déplacée comme sa propre classe:

Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyPkg.CampaignCounters;Property lastProcessingFinishedAt As%Library.PosixTime;Property lastProcessingStartedAt As%Library.PosixTime;Property lastProcessingStateChangeAt As%Library.PosixTime;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%Library.PosixTime;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyPkg.EmailCampaign) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statuschange As%Integer;Property bounce As%Integer;Property mta_dropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

Après avoir collé ce qui précède dans VSCode et l'avoir compilé, le compilateur a signalé que le type nommé "%Object" n'existe pas dans ObjectScript. v %JSONImport() de %JSON.Adaptor est flexible:

“L'argument d'entrée est soit JSON en tant que chaîne ou flux, soit une sous-classe de %DynamicAbstractObject.

Après avoir supprimé la propriété "As %Object" pour le premier paramètre de %OnJSONImport() dans la première classe et avoir séparé les deux classes dans des fichiers distincts, la classe MyPkg.EmailCampaign s'est compilée avec succès. Cependant, la classe MyPkg.CampaignCounters ne s'est pas compilée ; ChatGPT ne sait pas (pour l'instant ?) que l'underscore est un caractère spécial en ObjectScript (l'opérateur de concaténation de chaînes de caractères).  

Essayons de le lui enseigner.

Moi: Le caractère underscore "_" est un caractère spécial dans ObjectScript. Veuillez changer les propriétés dont le nom contient un underscore en camel case et supprimer l'underscore.

Reponse de ChatGPT:

Voici le code mis à jour avec les propriétés qui avaient des underscores dans leurs noms convertis en camel case:

Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor)
{
Property id As%Integer;Property appId As%Integer;Property appName As%String;Property contentId As%Integer;Property subject As%String;Property name As%String;Property counters As MyPkg.CampaignCounters;Property lastProcessingFinishedAt As%Library.PosixTime;Property lastProcessingStartedAt As%Library.PosixTime;Property lastProcessingStateChangeAt As%Library.PosixTime;Property numIncluded As%Integer;Property processingState As%String;Property scheduledAt As%Library.PosixTime;Property type As%String;ClassMethod%OnJSONImport(jsonObject As%Object, instance As MyPkg.EmailCampaign) As%Status
{
    Quit ..%JSONImport(jsonObject, .instance)
}
}

Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor)
{
Property processed As%Integer;Property deferred As%Integer;Property unsubscribed As%Integer;Property statusChange As%Integer;Property bounce As%Integer;Property mtaDropped As%Integer;Property dropped As%Integer;Property delivered As%Integer;Property sent As%Integer;Property click As%Integer;PropertyopenAs%Integer;
}

Avec une explication suivante:

ChatGPT s'est surpassé et a changé une autre propriété, nommée avec deux mots distincts, en camel case également (statuschange); je garde ce changement puisque d'autres propriétés nommées de la même manière dans la classe principale sont toutes en camel case.

Avec le dernier code généré dans deux fichiers séparés (et en supprimant la définition "As %Object" dans la définition de la méthode %OnJSONImport()), tout le code se compile avec succès.

Ces classes générées sont un excellent point de départ pour ce dont j'ai besoin et je répéterai le processus pour les autres sources de données REST de ce projet, ce qui me permettra d'éviter de nombreuses saisies inutiles.

0
0 27
Article Iryna Mykhailova · Mai 21, 2025 2m read

Bonjour la Communauté !

Je voulais vous partager mes impressions du Meetup FHIR France #13, organisé par Fyrstain et sponsorisé par InterSystems – et franchement, c’était une soirée inoubliable ! C’était une soirée riche en échanges, en apprentissages et en belles rencontres !

L’accueil a débuté à 19h et @Guillaume Rongier a aidé à enregistrer les participants

Après avoir laissé les participants arriver tranquillement, nous avons officiellement lancé la soirée. Fanch Rouault a souhaité la bienvenue au nom de Fyrstain

1
0 55
Article Iryna Mykhailova · Mai 2, 2025 3m read

Qui n'a jamais développé un bel exemple avec une image IRIS Docker et vu la génération de l'image échouer dans le Dockerfile parce que la licence sous laquelle l'image a été créée ne comportait pas certains privilèges ?

Dans mon cas, je déployais dans Docker une petite application utilisant le type de données Vector. Avec la version Community, ce n'est pas un problème, car elle inclut déjà la recherche et le stockage vectoriels. Cependant, lorsque j'ai remplacé l'image IRIS par une image IRIS classique (latest-cd), j'ai constaté que la compilation de l'image, y compris des classes générées, renvoyait l'erreur suivante :

0
0 24
Article Iryna Mykhailova · Avr 18, 2025 9m read

IRIS propose une fonctionnalité dédiée à la gestion des documents JSON, appelée DocDB.

Plateforme de données DocDB d'InterSystems IRIS® est une fonctionnalité permettant de stocker et de récupérer des données de base de données. Elle est compatible avec le stockage et la récupération de données de tables et de champs SQL traditionnels (classe et propriété), mais en est distincte. Elle est basée sur JSON (JavaScript Object Notation) qui prend en charge l'échange de données sur le Web. InterSystems IRIS prend en charge le développement de bases de données et d'applications DocDB en REST et en ObjectScript, ainsi que le support SQL pour la création ou l'interrogation de données DocDB.

De par sa nature, la base de données documentaire InterSystems IRIS est une structure de données sans schéma. Cela signifie que chaque document a sa propre structure, qui peut différer de celle des autres documents de la même base de données. Cela présente plusieurs avantages par rapport au SQL, qui nécessite une structure de données prédéfinie.

Le mot « document » est utilisé ici comme un terme technique spécifique à l'industrie, en tant que structure de stockage de données dynamique. Le « document », tel qu'utilisé dans DocDB, ne doit pas être confondu avec un document textuel ou avec la documentation.

Voyons comment DocDB peut permettre de stocker JSON dans la base de données et de l'intégrer dans des projets qui reposent uniquement sur des protocoles xDBC.

Commençons!

DocDB définit deux composants clés:

  • %DocDB.Database - Bien qu'il s'attende à la création d'une "base de données", ce qui peut prêter à confusion puisque nous avons déjà une base de données en termes SQL, il s'agit essentiellement d'une classe en ObjectScript. Pour ceux qui sont plus familiers avec SQL, elle fonctionne comme une table.
  • %DocDB.Document - Classe de base pour une 'base de données' qui étend la classe %Persistent et introduit des propriétés spécifiques à DocDB:
    • %DocumentId - IdKey
    • %Doc As %DynamicAbstractObject - Le stockage actuel du document JSON
    • %LastModified - Un horodatage mis à jour automatiquement pour chaque insertion et mise à jour

Création d'une table (base de données)

Passons maintenant à la création de notre première table, ou plutôt de notre première « base de données ». Il semble que l'on ne s'attendait pas à la création d'une base de données DocDB.Database uniquement à l'aide de SQL. Par conséquent, il n'est pas possible de créer une nouvelle « base de données » en utilisant uniquement SQL. Pour le vérifier, nous allons utiliser une approche ObjectScript classique. Voici un exemple de définition d'une classe qui étend %DocDB.Document:

Class User.docdb Extends%DocDB.Document [ DdlAllowed ]
{

}

La vérification de la table nouvellement créée à l'aide de SQL permet de s'assurer de son bon fonctionnement.

Il est temps de faire un premier essai et d'insérer quelques données

Nous pouvons insérer n'importe quelles données sans validation, ce qui signifie qu'il n'y a aucune restriction sur ce qui peut être inséré dans %Doc. La mise en place d'une validation serait bénéfique.

Extraction de valeurs d'un document

La base de données %DocDB.Database permet d'extraire des propriétés des documents et de les rendre disponibles sous forme de colonnes dédiées. Cela permet également d'effectuer une indexation sur ces propriétés.

Il faudrait d'abord obtenir la base de données.

USER>set docdb=##class(%DocDB.Database).%GetDatabase("User.docdb")

<THROW>%GetDatabase+5^%DocDB.Database.1 *%Exception.StatusException ERROR #25351: DocDB Database 'User.docdb' does not exist.

USER 2e1>w $SYSTEM.DocDB.Exists("User.docdb") 0

Euh, la base de données "n'existe pas", d'accord, créons-la alors

USER>set docdb=##class(%DocDB.Database).%CreateDatabase("User.docdb")

<THROW>%CreateDatabase+13^%DocDB.Database.1 *%Exception.StatusException ERROR #25070: The generated class name for the database 'User.docdb' conflicts with another class: User.docdb USER 2e1>

Ainsi, une simple définition de classe ne suffit pas. Il faut utiliser %DocDB.Database dès le début, ce qui n'est pas pratique, surtout lors de l'utilisation du contrôle de code source.

Pour résoudre ce problème, nous supprimons la classe existante et créons correctement la base de données:

USER>do $system.OBJ.Delete("User.docdb")

Deleting class User.docdb USER>set docdb=##class(%DocDB.Database).%CreateDatabase("User.docdb")

USER>zwrite docdb docdb=6@%DocDB.Database ; <OREF,refs=1> +----------------- general information --------------- | oref value: 6 | class name: %DocDB.Database | %%OID: $lb("3","%DocDB.Database") | reference count: 1 +----------------- attribute values ------------------ | %Concurrency = 1 <Set> | ClassName = "User.docdb" | DocumentType = "" | Name = "User.docdb" | Resource = "" | SqlNameQualified = "SQLUser.docdb" +-----------------------------------------------------

Cette fois, cela fonctionne et les données précédemment insérées restent intactes.

Supposons que nous ayons un document comme celui-ci

{"name":"test", "some_value":12345}

Extrayons ces deux champs à l'aide de la méthode %CreateProperty

USER>do docdb.%CreateProperty("name","%String","$.name",0)

USER>do docdb.%CreateProperty("someValue","%String","$.some_value",0)

Et vérifions la table

En vérifiant cette table, nous constatons que deux nouvelles colonnes ont été ajoutées, mais que celles-ci contiennent des valeurs nulles. Il semble que ces propriétés ne s'appliquent pas rétroactivement aux données existantes. Si un développeur ajoute ultérieurement des propriétés et des index à des fins d'optimisation, les données existantes ne refléteront pas automatiquement ces modifications.

Mettez à jour en utilisant la même valeur et vérifiez si %doc est json. Et nous obtenons notre valeur.

Jetons maintenant un coup d'œil à la classe, qui est entièrement créée et mise à jour par %DocDB.Database

Class User.docdb Extends%DocDB.Document [ Owner = {irisowner}, ProcedureBlock ]
{

Property name As%String [ SqlComputeCode = { set {}=$$%EvaluatePathOne^%DocDB.Document({%Doc},"$.name") }, SqlComputed, SqlComputeOnChange = %Doc ];Property someValue As%String [ SqlComputeCode = { set {}=$$%EvaluatePathOne^%DocDB.Document({%Doc},"$.some_value") }, SqlComputed, SqlComputeOnChange = %Doc ]; Index name On name; Index someValue On someValue; }

Ainsi, les propriétés créées contiennent un code pour extraire la valeur de %Doc, et oui, elles ne sont remplies que lorsque %Doc est modifié. Et des index ont été créés pour les deux champs, sans que personne ne le demande. Le fait d'avoir de nombreuses valeurs extraites augmentera l'utilisation des variables globales simplement par le nombre d'index.

Il sera possible de mettre à jour ces propriétés créées, sans nuire au %Doc original, mais les valeurs deviendront inutiles.

 
Insertion de données non valides
Insert NULL

Chaîne vide ou tout texte non-json.

Réponse franchement moche, rien à ajouter, la validation des tentatives d'insertion de quelque chose d'illégal dans une table semble raisonnable, donc, le message d'erreur serait au moins quelque chose de significatif. 

La base de données %DocDB.Database avec une méthode %GetProperty

USER>zw docdb.%GetPropertyDefinition("someValue")

{"Name":"someValue","Type":"%Library.String"}  ; <DYNAMIC OBJECT> USER>zw docdb.%GetPropertyDefinition("name")

{"Name":"name","Type":"%Library.String"}  ; <DYNAMIC OBJECT>

Le chemin d'accès à la valeur qui a été utilisé dans %CreateProperty a disparu, il n'y a aucun moyen de le valider. Si le chemin d'accès est incorrect, pour le mettre à jour, il faut d'abord appeler %DropProperty puis à nouveau %CreateProperty.

%FindDocuments

%%DocDB.Database vous permet de rechercher des documents

Pour trouver un ou plusieurs documents dans une base de données et renvoyer ceux-ci au format JSON, appelez la méthode %FindDocuments(). Cette méthode accepte n'importe quelle combinaison de trois prédicats positionnels facultatifs : une matrice de restriction, une matrice de projection et une paire clé/valeur limite.

Plus important encore, %FindDocuments ne se soucie pas de %Doc lui-même, il ne fonctionne que sur les propriétés. Assez fragile, il lève des exceptions sur tout ce qui ne correspond pas à ce qui est attendu. En fait, il construit simplement une requête SQL et l'exécute.

USER>do docdb.%FindDocuments(["firstName","B","%STARTSWITH"]).%ToJSON() 

<THROW>%FindDocuments+37^%DocDB.Database.1 *%Exception.StatusException ERROR #25541: DocDB Property 'firstName' does not exist in 'User.docdb'

USER>do docdb.%FindDocuments(["name","test","in"],["name"]).%ToJSON()

{"sqlcode":100,"message":null,"content":[{"name":"test"}]} USER>do docdb.%FindDocuments(["name","","in"],["name"]).%ToJSON()

<THROW>%FindDocuments+37^%DocDB.Database.1 *%Exception.SQL -12 -12   A term expected, beginning with either of:  identifier, constant, aggregate, $$, (, :, +, -, %ALPHAUP, %EXACT, %MVR %SQLSTRING, %SQLUPPER, %STRING, %TRUNCATE, or %UPPER^ SELECT name FROM SQLUser . docdb WHERE name IN ( )

USER>do docdb.%FindDocuments(["name","test","="]).%ToJSON()

{"sqlcode":100,"message":null,"content":[{"%Doc":"{"name":"test", "some_value":12345}","%DocumentId":"1","%LastModified":"2025-02-05 12:25:02.405"}]} USER 2e1>do docdb.%FindDocuments(["Name","test","="]).%ToJSON()

<THROW>%FindDocuments+37^%DocDB.Database.1 *%Exception.StatusException ERROR #25541: DocDB Property 'Name' does not exist in 'User.docdb'

USER>do docdb.%FindDocuments(["%Doc","JSON","IS"]).%ToJSON()

<THROW>%FindDocuments+37^%DocDB.Database.1 *%Exception.StatusException ERROR #25540: DocDB Comparison operator is not valid: 'IS' USER 2e1>do docdb.%FindDocuments(["%Doc","","IS JSON"]).%ToJSON()

<THROW>%FindDocuments+37^%DocDB.Database.1 *%Exception.StatusException ERROR #25540: DocDB Comparison operator is not valid: 'IS JSON'

L'utilisation de SQL simple serait bien plus fiable 

Enregistrement

Un autre aspect très intéressant est l'efficacité avec laquelle JSON est enregistré dans la base de données.

^poCN.bvx3.1(1)=$lb("","2025-02-05 12:25:02.405","test",12345)
^poCN.bvx3.1(1,"%Doc")="{""name"":""test"", ""some_value"":12345}"
^poCN.bvx3.1(2)=$lb("","2025-02-05 12:25:02.405")
^poCN.bvx3.1(2,"%Doc")="[1,2,3]"
^poCN.bvx3.1(3)=$lb("","2025-02-05 12:01:18.542")
^poCN.bvx3.1(3,"%Doc")="test"
^poCN.bvx3.1(4)=$lb("","2025-02-05 12:01:19.445")
^poCN.bvx3.1(4,"%Doc")=$c(0)
^poCN.bvx3.1(5)=$lb("","2025-02-05 12:01:20.794")

Le JSON est stocké sous forme de texte brut, tandis que d'autres bases de données utilisent des formats binaires pour un enregistrement et une recherche plus efficaces. La base de données DocDB d'IRIS ne prend pas en charge la recherche directe dans le contenu des documents, sauf si JSON_TABLE est utilisé, ce qui nécessite tout de même l'analyse du JSON dans un format binaire interne.

Dans la version 2025.1, %DynamicAbstractObject introduit les méthodes %ToPVA et %FromPVA, ce qui semble enregistrer le JSON dans un format binaire.

USER>do ({"name":"value"}).%ToPVA($name(^JSON.Data(1))) 

USER>zw ^JSON.Data ^JSON.Data(1,0,0)="PVA1"$c(134,0,6,0,2,0,0,0,0,0,14,0,15,0,2,0,21,9,6,136,0,1,6,0,1,0,2,1,137,0,1,5,8,1,6)"value"$c(6,0,6)"name"_$c(5)

USER>zw {}.%FromPVA($name(^JSON.Data(1)))

{"name":"value"}  ; <DYNAMIC OBJECT,refs=1>

Cependant, le traitement de certaines structures présente des incohérences.

USER>do ({}).%ToPVA($name(^JSON.Data(1)))

<SYSTEM>%ToPVA+1^%Library.DynamicAbstractObject.1

USER>do ({"name":{}}).%ToPVA($name(^JSON.Data(1)))

<SYSTEM>%ToPVA+1^%Library.DynamicAbstractObject.1

Conclusion

Actuellement, %DocDB n'est vraiment pratique qu'avec ObjectScript et a des limites en SQL. Des problèmes de performances apparaissent lorsqu'il s'agit de traiter de grands ensembles de données. Tout ce que %DocDB offre peut être réalisé en utilisant du SQL de base tout en conservant un support SQL complet. Compte tenu de l'implémentation actuelle, il y a peu d'intérêt à utiliser DocDB plutôt que des approches SQL de base.

0
0 46
Article Iryna Mykhailova · Avr 10, 2025 3m read

Le mappeur d'enregistrements complexes peut vous aider à transformer des données de fichiers texte composées de différents types d'enregistrements en messages persistants dans IRIS. Pour comprendre les bases du mappeur d'enregistrements complexes et découvrir un exemple de mise en œuvre en production, visionnez la vidéo des services d'apprentissage.

0
0 32
Article Iryna Mykhailova · Avr 9, 2025 3m read

Comme vous le savez peut-être, le serveur web privé fourni avec chaque installation IRIS a été supprimé, rendant nécessaire l'utilisation d'un serveur web externe.

Pour les déploiements Docker, il est courant d'utiliser l'image de passerelle web (disponible ici) avec l'image IRIS pour accéder facilement au Portail de Gestion. Cette image peut être configurée pour un accès via HTTPS sans problème en configurant les certificats.

0
0 54
Article Iryna Mykhailova · Avr 7, 2025 11m read

Lors de la création d'un bundle à partir de données héritées, je (et d'autres) souhaitais pouvoir contrôler si les ressources étaient générées avec une méthode de requête FHIR PUT plutôt qu'avec la méthode POST codée en dur. J'ai étendu les deux classes responsables de la transformation de SDA en FHIR dans une production d'interopérabilité afin de prendre en charge un paramètre permettant à l'utilisateur de contrôler la méthode de requête.

0
0 19
Article Iryna Mykhailova · Avr 3, 2025 3m read

Introduction

Dans InterSystems IRIS 2024.3 et les versions ultérieures d'IRIS, le composant AutoML est désormais fourni sous forme de package Python distinct, installé après l'installation. Malheureusement, certaines versions récentes des packages Python sur lesquels AutoML s'appuie ont introduit des incompatibilités et peuvent entraîner des échecs lors de l'entraînement des modèles (instruction TRAIN MODEL). Si vous rencontrez une erreur mentionnant « TypeError » et l'argument de mot-clé « fit_params » ou « sklearn_tags », lisez la suite pour une solution rapide.

Cause principale

0
0 28
Article Iryna Mykhailova · Mars 17, 2025 3m read

   

Contrairement au film mentionné dans l'image (pour ceux qui ne connaissent pas, Matrix, 1999), le choix entre Dynamic SQL et Embedded SQL n'est pas un choix entre réalité et fantaisie, mais une décision à prendre. Ci-dessous, je vais essayer de vous faciliter la tâche.

Si votre besoin concerne les interactions entre le client et l'application (et par conséquent la base de données), le Dynamic SQL peut être plus approprié, car il s'adapte très facilement à ces changements de requête. Cependant, ce dynamisme a un coût : à chaque nouvelle requête, elle est remodelée, ce qui peut entraîner un coût d'exécution plus élevé. Voici un exemple simple d'extrait de code Python.

0
0 38
Article Iryna Mykhailova · Mars 13, 2025 3m read

Présentation

Grâce à SQL, vous pouvez créer, entraîner et gérer des modèles de machine learning directement dans la base de données grâce à l'outil robuste IntegratedML d'InterSystems IRIS. À l'aide d'exemples SQL représentant vos données, nous aborderons dans cet article la configuration d'IntegratedML et son utilisation pratique.

Configuration d'IntegratedML

0
0 36
Article Iryna Mykhailova · Mars 11, 2025 1m read

Bonjour collègues !

Lors du développement d'une application front-end ou de toute autre communication avec l'API REST, il est souvent judicieux d'utiliser une Swagger UI, une interface de test pour l'API REST conforme à la spécification Open API 2.0. Elle est généralement très pratique, car elle permet d'effectuer des tests manuels rapides avec l'API REST, ses réponses et les données qu'elle contient.

J'ai récemment intégré la prise en charge de Swagger dans InterSystems IRIS FHIR template pour l'API FHIR R4 :

Comment le faire fonctionner.

0
0 32
Article Iryna Mykhailova · Mars 7, 2025 3m read

Je me suis lancé un défi : trouver un moyen de faire en sorte qu'une variable se surveille elle-même pour une certaine valeur et fasse quelque chose lorsqu'elle atteint cette valeur sans avoir à la vérifier à chaque fois que quelque chose la touche. En gros, un moyen de dire "à un moment donné pendant l'exécution de ce code, si x = 0 (ou quelle que soit la condition) faire ceci". La classe avec laquelle j'ai fini par surveiller un %Status :

0
0 42
Article Iryna Mykhailova · Fév 24, 2025 3m read

Bonjour chers membres de la communauté !

Il est très courant dans la vie quotidienne des utilisateurs d'IRIS ou de Health Connect qu'il soit nécessaire d'installer de nouvelles instances ou de mettre à jour celles qu'ils possèdent déjà et dans de nombreux cas, ce ne sont pas ces mêmes utilisateurs qui effectuent l'installation, mais plutôt le personnel des systèmes qui souvent ne tient pas compte des particularités de l'attribution des autorisations nécessaires à l'installation.

0
0 34