#InterSystems IRIS

0 Abonnés · 713 Publications

InterSystems IRIS est une plateforme complète de données
InterSystems IRIS vous offre tout le nécessaire pour capturer, partager, comprendre et agir sur la ressource le plus précieuse de votre entreprise : vos données.
En tant que plateforme complète, InterSystems IRIS élimine la nécessité d'intégrer plusieurs technologies de développement. Les applications nécessitent moins de code, moins de ressources système et moins de maintenance.

Article Robert Cemper · Sept 6, 2025 4m read

Cet article a été motivé par le concours Article Bounty de septembre 2025.
***************************************************************************

Le principe de Docker me semble tout simplement convaincant.

  • Obtenez un bac à sable dans lequel vous pouvez jouer et essayer tout ce que vous voulez/devez faire.
  • Une fois terminé, vous le supprimez sans laisser de traces dans votre environnement de travail.

C'est sur cette base technique que j'ai pu effectuer environ 700 évaluations dans OEX 
sans pratiquement aucun effet secondaire  (à l'exception de ceux causés par moi-même).

0
0 62
Article Guillaume Rongier · Sept 4, 2025 6m read

img

Les modules, quel sujet ! Nous n'avons pas de notion équivalente en ObjectScript, mais c'est un concept fondamental en Python. Découvrons-le ensemble.

Qu'est-ce qu'un module?

Je considère les modules comme une couche intermédiaire entre les classes et les packages. Voici un exemple.

Un mauvais exemple :

# MyClass.py
class MyClass:
    def my_method(self):
        print("Hello from MyClass!")

Lorsque vous voulez utiliser cette classe dans un autre script, vous devez faire comme suit:

# class_usage.py
from MyClass import MyClass # weird, right?

my_instance = MyClass()
my_instance.my_method()

En quoi cet exemple est-il mauvais?

Tout d'abord, parce que les noms de fichiers doivent être écrits en snake_case conformément à la norme PEP 8, et qu'ils doivent donc être nommés my_class.py. Ensuite, parce que vous importez une classe à partir d'un fichier qui porte le même nom que la classe. Ce n'est pas une bonne pratique en Python.

Je sais que cela peut être déroutant, surtout si vous venez d'ObjectScript où les classes sont définies dans des fichiers portant le même nom que celui de la classe.

Notions avancées

Un module est un fichier Python

Donc, nous venons de voir que les modules peuvent être des fichiers Python, mais sans l'extension .py.

Mais attendez, cela signifie-t-il qu'un script python est également un module? Oui, tout à fait!

C'est pourquoi il faut être prudent lorsque vous importez un script, car celui-ci exécutera le code qui y est contenu. Consultez l'article Introduction à Python pour en savoir plus.

Un module est un dossier contenant un fichier __init__.py

Comment ça, un dossier peut être un module? Oui, c'est possible!

Un dossier peut être un module s'il contient un fichier __init__.py. Ce fichier peut être vide ou contenir le code d'initialisation du module.

Voici un exemple:

src/python/article/
└── my_folder_module/
    ├── __init__.py
    ├── my_sub_module.py
    └── another_sub_module.py
# my_folder_module/my_sub_module.py
class MySubModule:
    def my_method(self):
        print("Hello from MySubModule!")
# my_folder_module/another_sub_module.py
class AnotherSubModule:
    def another_method(self):
        print("Hello from AnotherSubModule!")
# my_folder_module/__init__.py
# Ce fichier peut être vide ou contenir le code d'initialisation du module.

Dans ce cas, my_folder_module est un module, et vous pouvez l'importer comme suit:

from my_folder_module import my_sub_module, another_sub_module

Ou si vous définissez un fichier __init__.py avec le contenu suivant:

# my_folder_module/__init__.py
from .my_sub_module import MySubModule
from .another_sub_module import AnotherSubModule

Vous pouvez l'importer de la manière suivante:

from my_folder_module import MySubModule, AnotherSubModule

Vous voyez la subtilité ? Vous pouvez importer les classes directement depuis le module sans spécifier le sous-module, car le fichier __init__.py est exécuté lorsque vous importez le module, et ce fichier peut définir les éléments disponibles dans l'espace de noms du module.

sys.path

Lorsque vous importez un module, Python le recherche dans les répertoires spécifiés dans le fichier sys.path. Il s'agit d'une liste de chaînes de caractères qui spécifie le chemin de recherche des modules.

Vous pouvez afficher le sys.path actuel en exécutant le code suivant:

import sys
print(sys.path)

Par défaut, le répertoire actuel ainsi que d'autres répertoires variés en fonction de votre installation Python sont inclus.

Vous pouvez également ajouter des répertoires à sys.path lors de l'exécution, ce qui est utile lorsque vous souhaitez importer des modules à partir d'un emplacement spécifique. Par exemple:

import sys
sys.path.append('/path/to/your/module')
from your_module import YourClass

Pour cette raison, dans l'article précédent, nous avons ajouté le chemin d'accès au module avant de l'importer:

Set sys = ##class(%SYS.Python).Import("sys")
do sys.path.append("/irisdev/app/src/python/article")
set my_module = ##class(%SYS.Python).Import("my_module")

sys.path et autres répertoires

Quels sont les autres répertoires dans sys.path? Il s'agit généralement des répertoires suivants:

  • Le répertoire contenant le script d'entrée (ou le répertoire actuel si aucun script n'est spécifié).
  • Les répertoires de la bibliothèque standard, qui contiennent les modules intégrés fournis avec Python.
  • Les répertoires site-packages contenant les packages tiers installés.

site-packages

Voici comment fonctionne site-packages. Lorsque vous installez un package à l'aide de pip, il est installé dans le répertoire site-packages, qui est automatiquement inclus dans sys.path. Cela vous permet d'importer le package sans avoir à spécifier son emplacement.

🤨🔍 Mais comment et où le répertoire site-packages est-il défini, et par qui?

Le répertoire site-packages est créé lors de l'installation de Python et se trouve généralement dans le répertoire lib de votre installation Python. Son emplacement exact dépend de votre système opérationnel et de la manière dont Python a été installé.

Par exemple, sur une installation Linux classique, le répertoire site-packages peut être accessible à l'emplacement suivant:

/usr/local/lib/python3.x/site-packages

Sous Windows, il peut se trouver à l'emplacement suivant:

C:\Python3x\Lib\site-packages

Lorsque vous installez un package à l'aide de pip, il est installé dans le répertoire site-packages, qui est automatiquement inclus dans sys.path. Cela vous permet d'importer le package sans avoir à spécifier son emplacement.

import site
print(site.getsitepackages())

🤨🔍 A quel moment et à quel endroit l'interpréteur Python lit-il le fichier site.py?

Le fichier site.py (qui se trouve dans le répertoire standard de la bibliothèque) est exécuté automatiquement au lancement de l'interpréteur Python. Il est responsable de la configuration du répertoire site-packages et de son ajout à sys.path. Ce fichier se trouve dans le répertoire standard de la bibliothèque de votre installation Python.

sys.path dans IRIS

Dans IRIS, nous avons également un fichier site.py, qui se trouve dans <installation_directory>/lib/python/iris_site.py. Ce fichier est exécuté lorsque vous démarrez ou importez un script/module dans IRIS, et il configure le sys.path pour vous.

En résumé, le fichier iris_site.py effectue les opérations suivantes:

  • il préserve le répertoire site-packages par défaut
  • il ajoute le répertoire <installation_directory>/lib/python/ à sys.path
    • c'est là que les modules Python IRIS sont situés, veuillez ne pas y placer vos modules
  • il ajoute le répertoire <installation_directory>/mgr/python/ à sys.path
    • c'est là que vous pouvez placer vos modules Python personnalisés
  • il ajoute la chaîne de configuration PythonPath à sys.path

Conclusion

Un module peut être:

  • un fichier Python (avec ou sans l'extension .py)
  • un dossier contenant un fichier __init__.py
  • un script Python (qui est également un module)
  • si vous ne réussissez pas à importer un module, vérifiez s'il se trouve dans la liste sys.path
0
0 21
Article Eugene.Forde · Sept 4, 2025 4m read

Salut tout le monde !

Parfois, lorsqu’on conçoit une méthode de classe et qu’on y ajoute de plus en plus de fonctionnalités utiles, le nombre de paramètres peut rapidement atteindre 10, voire plus.

Cela devient alors assez difficile pour les utilisateurs de ces méthodes utiles de se rappeler de la position des paramètres importants, et il devient très facile de se tromper en inversant des valeurs entre paramètres.

Voici un exemple d’une telle méthode (j’ai demandé à GPT de me créer une méthode avec 20 paramètres) :

ClassMethod GenerateReportWith20Params(
    pTitle As%String = "",
    pAuthor As%String = "",
    pDate As%String = "",            // ex. 2025-09-03
    pCompany As%String = "",
    pDepartment As%String = "",
    pVersion As%String = "1.0",
    pFormat As%String = "pdf",      // pdf|html|docx
    pIncludeCharts As%Boolean = 1,
    pIncludeSummary As%Boolean = 1,
    pIncludeAppendix As%Boolean = 0,
    pConfidentiality As%String = "Public",
    pLanguage As%String = "en",
    pReviewers As%String = "",      // CSV, ex. "Alice,Bob"
    pApprover As%String = "",
    pLogoPath As%String = "",
    pWatermarkText As%String = "",
    pColorScheme As%String = "default",
    pPageSize As%String = "A4",
    pOrientation As%String = "Portrait",
    pOutputPath As%String = "report.pdf"
) As%Status
{
    // implémentation
}
0
0 21
Article Guillaume Rongier · Sept 3, 2025 3m read

img

Ce court article traite du PEP 8, le guide de style Python.

Qu'est-ce que le PEP 8?

En bref, le PEP 8 fournit des directives et des bonnes pratiques pour écrire du code Python.

  • les noms de variables doivent être en snake_case
  • les noms de classes doivent être en CamelCase
  • les noms de fonctions doivent être en snake_case
  • les constantes doivent être en UPPER_CASE
  • l'indentation doit être de 4 espaces (pas de tabulations)
  • les variables/fonctions privées doivent commencer par un trait de soulignement (_)
    • puisque les variables et fonctions privées n'existent pas en Python, il s'agit simplement d'une convention
  • votre script ne doit pas s'exécuter lorsqu'il est importé
    • rappelez-vous que lorsque vous importez un script, le code est exécuté, consultez le premier article
  • ...

Inutile de tout citer, mais gardez bien à l'esprit que cela vous aidera à comprendre le code de vos collègues et permettra à ces derniers de comprendre le vôtre ^^.

Vous avez peut-être déjà entendu parler du terme pythonic. Suivre les recommandations du PEP 8 est un moyen d'écrire du code Python considéré comme pythonique (ce n'est pas la seule méthode, mais cela en fait partie).

Pourquoi les instructions du PEP 8 sont-elles importantes et pertinentes pour les développeurs Python d'IRIS?

Dans IRIS, et en particulier dans ObjectScript, nous avons également un guide de style, qui repose principalement sur le camelCase pour les noms de variables et le PascalCase pour les noms de classes.

Malheureusement, PEP 8 recommande d'utiliser le snake_case pour les noms de variables et de fonctions.

Et comme vous le savez déjà, dans ObjectScript, le trait de soulignement (_) sert à la concaténation et ne nous convient évidemment pas.

Comment contourner ce problème ? Utilisez des guillemets doubles pour appeler les noms de variables/fonctions Python dans le code ObjectScript.

Exemple:

Class Article.PEP8Example Extends %RegisteredObject
{

ClassMethod Run()
{
    Set sys = ##class(%SYS.Python).Import("sys")
    do sys.path.append("/irisdev/app/src/python/article")
    set pep8Example = ##class(%SYS.Python).Import("pep8_example")
    do pep8Example."my_function"() // Observez les guillemets doubles autour du nom de la fonction
}

}

Cette commande appellera la fonction my_function dans le fichier pep8_example.py, qui est définie comme suit:

# src/python/article/pep8_example.py
def my_function():
    print("Hello, World!")

Lorsque vous exécutez la méthode Run de la classe Article.PEP8Example, le résultat suivant s'affiche:

iris session iris -U IRISAPP '##class(Article.PEP8Example).Run()'
Hello, World!

Ça y est!

0
0 37
Article Guillaume Rongier · Sept 2, 2025 6m read

img

Cet article présente une introduction à la programmation Python dans le contexte d'IRIS.

Avant toute chose, je vais aborder un sujet important : Fonctionnement de Python. Cela vous aidera à comprendre certains problèmes et certaines limites que vous pourriez rencontrer lorsque vous utilisez Python dans IRIS.

Tous les articles et exemples sont disponibles dans ce dépôt git: iris-python-article

Fonctionnement de Python

Langage interprété

Python est un langage interprété, ce qui signifie que le code est exécuté ligne par ligne lors de l'exécution, même lorsque vous importez un script.

Qu'est-ce que cela veut dire? Examinons le code suivant:

# introduction.py

def my_function():
    print("Hello, World!")

my_function()

Lorsque vous exécutez ce script, l'interpréteur Python lit le code ligne par ligne. Il définit d'abord la fonction my_function, puis appelle cette fonction, qui affiche "Hello, World!" à la console.

Exemple d'exécution directe du script:

python3 /irisdev/app/src/python/article/introduction.py 

Cela donnera le résultat suivant:

Hello, World!

Dans un contexte IRIS, que se passera-t-il si nous importons ce script ?

Class Article.Introduction Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")

        do ##class(%SYS.Python).Import("introduction")
    }
}

Lançons-le:

iris session iris -U IRISAPP '##class(Article.Introduction).Run()'

Cela donnera le résultat suivant:

Hello, World!

En effet, l'interpréteur Python importe le code en l'interprétant, il définit d'abord la fonction, puis l'appelle, exactement comme si vous exécutiez le script directement mais vous ne l'exécutez pas, vous l'importez.

⚠️ Remarque importante : si vous importez le script sans appeler la fonction, rien ne se passera. La fonction est définie, mais elle ne s'exécutera pas jusqu'à ce que vous l'appeliez explicitement.

Compris? L'interpréteur Python exécute le code dans le fichier, et si vous n'appelez pas la fonction, elle ne s'exécutera pas.

Exemple d'importation sans appel:

# introduction1.py
def my_function():
    print("Hello, World!")

Lançons-le dans un interpréteur Python:

python3 /irisdev/app/src/python/article/introduction1.py 

Résultat:

# Aucun résultat, puisque la fonction est définie mais n'est pas appelée

Dans un contexte IRIS, si vous importez ce script:

Class Article.Introduction1 Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")
        do ##class(%SYS.Python).Import("introduction1")
    }
}

Lançons-le:

iris session iris -U IRISAPP '##class(Article.Introduction1).Run()'

Vous ne verrez aucun résultat,puisque la fonction est définie mais n'est pas appelée.

🤯 Pourquoi cette nuance est-elle importante?

  • Lorsque vous importez un script Python, celui-ci exécute le code contenu dedans.
    • Vous ne souhaitez peut-être pas que cela se produise
  • Vous pouvez être dérouté en pensant que l'importation d'un script revient à l'exécuter, mais ce n'est pas le cas.

Mise en cache des importations

Lorsque vous importez un script Python, l'interpréteur Python met en cache le script importé. Cela signifie que si vous importez à nouveau le même script, il ne réexécutera pas le code de ce script, mais utilisera la version mise en cache.

Exemple:

Réutilisons le script introduction.py:

# introduction.py
def my_function():
    print("Hello, World!")

my_function()

Maintenant, faisons la même chose en réutilisant la classe Article.Introduction:

Class Article.Introduction Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")
        do ##class(%SYS.Python).Import("introduction")
    }
}

Mais maintenant, nous allons l'exécuter deux fois de suite dans la même session IRIS:

iris session iris -U IRISAPP 

IRISAPP>do ##class(Article.Introduction).Run()
Hello, World!

IRISAPP>do ##class(Article.Introduction).Run()

IRISAPP>

🤯 Mais qu'est-ce que c'est que ça ?

Oui, Hello, World! n'est imprimé qu'une seule fois !

⚠️ Votre script importé est mis en cache. Cela signifie que si vous modifiez le script après l'avoir importé, les modifications ne seront pas prises en compte tant que vous n'aurez pas modifié la session IRIS.

Cela vaut également si vous utilisez la balise de langage python language tag dans IRIS:

Class Article.Introduction2 Extends %RegisteredObject
{

ClassMethod Run() [ Language = python ]
{
    import os

    if not hasattr(os, 'foo'):
        os.foo = "bar"
    else:
        print("os.foo already exists:", os.foo)
}

}

Lançons-le:

iris session iris -U IRISAPP

IRISAPP>do ##class(Article.Introduction2).Run()

IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar

Oh non, le module os est mis en cache et l'attribut foo n'est pas redéfini comme inexistant.

Conclusion

J'espère que cette introduction sera utile pour comprendre pourquoi, lorsque vous travaillez avec Python dans IRIS, vous pouvez rencontrer des comportements inattendus, notamment lors de l'importation de scripts et de la mise en cache.

À retenir lorsque vous travaillez avec Python dans IRIS:

  • Changez à chaque fois de session IRIS pour voir les modifications apportées à vos scripts Python.
    • Il ne s'agit pas d'un bug, c'est le fonctionnement normal de Python.
  • N'oubliez pas que l'importation d'un script exécute son code.

Bonus

Attendez! C'est illogique, si vous dites que lorsque vous importez un script, il est mis en cache. Pourquoi, lorsque je travaille avec la balise language tag = python, lorsque je modifie le script, cela fonctionne sans changer la session IRIS?

Bonne question, cela est dû au fait que la balise language tag est conçue pour que, chaque fois que vous l'exécutez, elle relise le script et l'exécute ligne par ligne comme s'il s'agissait de nouvelles lignes dans un interpréteur Python natif. La balise language tag n'importe pas le script, elle l'exécute simplement comme si vous l'exécutiez directement dans un interpréteur Python sans le relancer.

Exemple:

Class Article.Introduction2 Extends %RegisteredObject
{
ClassMethod Run() [ Language = python ]
{
    import os

    if not hasattr(os, 'foo'):
        os.foo = "bar"
    else:
        print("os.foo already exists:", os.foo)
}
}

Lançons-le:

iris session iris -U IRISAPP
IRISAPP>do ##class(Article.Introduction2).Run()

IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar  

Dans un interpréteur Python, cela se présentera comme suit:

import os

if not hasattr(os, 'foo'):
    os.foo = "bar"
else:
    print("os.foo already exists:", os.foo)

import os
if not hasattr(os, 'foo'):
    os.foo = "bar"
else:
    print("os.foo already exists:", os.foo)

Résultat:

os.foo already exists: bar # only printed once

Maintenant, c'est clair?

Suite:

  • Pep8
  • Modules
  • Méthodes Dunder
  • Utilisation de Python dans IRIS
  • ...
0
0 23
Article Sylvain Guilbaud · Août 29, 2025 1m read

Rubrique FAQ InterSystems

Par défaut, l'ordre des colonnes d'une table est déterminé automatiquement par le système. Pour modifier cet ordre, définissez explicitement l'ordre de chaque propriété à l'aide du mot-clé SqlColumnNumber lors de la définition de la classe.

Exemple :

Property Name As %String [SqlColumnNumber = 2];

Veuillez consulter la documentation ci-dessous.

SqlColumnNumber

Si vous souhaitez modifier le nom de la table SQL, spécifiez SqlTableName. Si vous souhaitez modifier le nom de la colonne (nom du champ), spécifiez SqlFieldName.

0
0 16
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 Lorenzo Scalese · Août 27, 2025 4m read

Cet excellent article a récemment déclenché une discussion privée, et j'aimerais partager certaines de mes réflexions à ce sujet.
La question motivante se résume ainsi : pourquoi devons-nous absolument établir des règles ou des conventions de codage ? Où est passée la merveilleuse époque des artistes-programmeurs de la Renaissance qui traçaient leur propre voie, avant d'être supplantés par les artisans, puis (pire encore) par IA?
En bref, il existe plusieurs raisons pour expliquer l'utilité des normes et des directives de codage, et les artistes-programmeurs de la Renaissance n'ont pas complètement disparu.

Raison 1: De nos jours, lorsque vous enseignez à un artiste, à un peintre débutant, vous commencez par lui demander de colorier à l'intérieur des lignes. Ce peintre sera peut-être brillant un jour, mais pas encore. Si vous voulez apprendre à créer des œuvres d'art, vous devez d'abord faire cela, puis apprendre progressivement les techniques et les concepts des maîtres. Ensuite, si vous êtes vraiment doué ou si vous avez beaucoup de chance, vous pourrez développer votre propre style et créer quelque chose de nouveau que le reste du monde voudra imiter. Mais vous devez commencer par suivre les règles.

(remarque: j'ai trouvé cela sur l'Internet ; aucun de mes enfants ne sait encore faire ça.)

Raison 2: Si vous travaillez dans un domaine où votre créativité doit s'intégrer à celle des autres, comme dans un ouvrage de patchwork, vous devez établir certaines règles, sans quoi votre œuvre ne fonctionnera pas, car les pièces ne s'emboîteront jamais. Vous pouvez soit convenir du principe selon lequel "nous faisons tous des carrés de 40 cm", soit travailler encore plus étroitement et en collaboration avec un groupe d'autres personnes, ce qui n'est peut-être pas la meilleure option pour un artiste solitaire qui passe quatre ans à peindre un plafond. (Surtout si ses responsables décident de faire appel à quelques autres artistes solitaires pour "l'aider.”)

(C'est ChatGPT qui m'a inspiré cette image. Toutes mes excuses aux artistes réels.)

Raison 3: Si vous travaillez sur un élément de codage existant, particulièrement ancien ou particulièrement génial, vous êtes souvent confronté à un dilemme : comprendre et accepter l'intention et l'élégance de la conception et de l'exécution de l'auteur , ou simplement dire "C'est moi le responsable maintenant, on va tout jeter et faire à ma façon." Je vais vous donner un exemple : une application web sur laquelle j'ai travaillé se sert beaucoup de "pages XML" où un trio (pageName.xml, pageName.js, pageName.mac) forme une page. Le fichier pageName.xml était, jusqu'à récemment, probablement écrit dans cet étrange dialecte "WD-xsl" qui ressemble beaucoup au XSLT standard mais qui ne fonctionne que dans Internet Explorer Microsoft Edge s'il se fait passer pour IE5. Le fichier pageName.js contient probablement environ 40 fois plus de "frame" que nécessaire et comfortable. Le fichier pageName.mac est probablement rempli de syntaxe à points, de commandes abrégées et de macros incohérentes. Si vous êtes un développeur débutant, vous pleurez et vous fuyez parce que cela n'a aucun sens. Si vous êtes un développeur expérimenté, vous lisez le code, vous essayez de le comprendre, puis vous décidez que “c'est dégoûtant, je vais faire mieux autrement” – mais alors la personne qui travaillera ensuite sur l'application devra apprendre le paradigme original et votre nouveau paradigme astucieux. Faites cela pendant 20 ans, et un véritable cauchemar vous attend. Mais si vous êtes vraiment un artiste expert, vous pouvez vous lancer dans la restauration artistique: observez l'élégance de la structure d'origine et travaillez en respectant celle-ci, en corrigeant délicatement les éléments architecturaux sans importance qui font pleurer le nouveau développeur et poussent le développeur senior à appuyer sur "Supprimer" sans risquer de créer un chaos fragmenté ou de passer des années  à tout repeindre. Peut-être même réaliserez-vous vos propres œuvres dans le style du grand maître. La leçon la plus importante à tirer de ce conte est qu'en tant qu'artiste véritable, vous êtes tout à fait intéressé à produire un travail d'une telle qualité que quelqu'un qui en hériterait sans passer des années pour s'entraîner ne déciderait pas de tout jeter. Les "petites choses" comme le style de code, la lisibilité et la gestion de la dette technique contribuent grandement à préserver l'architecture et même chaque ligne de code.

En bref:

  • Les règles et les conventions nous permettent de former de nouveaux artistes brillants bien avant qu'ils ne le deviennent
  • Les règles et les conventions nous offrent un cadre fixe dans lequel nous pouvons exercer notre créativité sans perdre de temps à essayer de nous entendre avec les autres
  • Les règles et les conventions nous permettent de créer quelque chose de beau qui ne sera pas abandonné par la première personne qui en héritera
0
0 17
Article Guillaume Rongier · Août 25, 2025 8m read

Bonjour à toute la communauté InterSystems ! Je m'appelle Sidd, je suis stagiaire au bureau de Singapour et j'ai récemment eu l'occasion de développer un pilote afin de connecter IRIS à Metabase pour aider certains ingénieurs commerciaux ici. On m'a encouragé à partager cette information ici afin que ceux qui rencontrent un problème similaire puissent utiliser ce pilote et faire part de leurs commentaires sur les améliorations possibles. Le dépôt GitHub complet avec la procédure de démarrage rapide, un bref aperçu et le processus de création du pilote est disponible ici. L'objectif principal de cet article est d'approfondir le code principal du pilote et de discuter des idées de son amélioration.

Bref aperçu

La motivation à l'origine de ce pilote est assez simple : Metabase n'offre pas de prise en charge JDBC native pour InterSystems IRIS, nous avions donc besoin d'un pilote personnalisé pour combler l'écart entre le backend de Metabase et les bases de données IRIS. Sans ce pont, il n'y a tout simplement aucun moyen de connecter Metabase aux instances IRIS, ce qui pose évidemment un problème si vous essayez de créer des tableaux de bord et des analyses à partir de vos données IRIS.

La bonne nouvelle, c'est que comme IRIS est déjà fourni avec son propre pilote JDBC et que Metabase dispose d'une implémentation générique solide du pilote SQL JDBC, je n'ai pas eu à réinventer la roue. La plupart des tâches lourdes, telles que l'établissement des connexions à la base de données, le traitement des requêtes de base, la gestion des pools de connexions et les opérations SQL standard, ont pu être déléguées à l'infrastructure générique existante du pilote Metabase. Cela a permis d'économiser énormément de temps de développement et d'assurer la compatibilité avec les fonctionnalités principales de Metabase.

Remplacement des méthodes multiples

L'approche consistait essentiellement à démarrer un pilote minimal, à le tester par rapport à IRIS, à identifier les points faibles où des dysfonctionnements ou des comportements inattendus se produisaient, puis à remplacer de manière sélective ces méthodes par des implémentations spécifiques à IRIS.

Structure du pilote

La structure du pilote constitue la base en définissant les capacités SQL d'IRIS dans le contexte de Metabase et un système robuste de mappage des types qui traduit les types de données IRIS JDBC vers le système de types interne de Metabase.

Ceci est suivi de quelques fonctions DateTime qui permettent à Metabase de connaître la manière de générer le SQL nécessaire pour les visualisations de séries chronologiques. Cela n'a pas encore été testé de manière rigoureuse, mais plutôt implémenté en s'inspirant d'implémentations similaires dans d'autres pilotes JDBC.

Connexion à IRIS

L'une des méthodes les plus importantes à remplacer serait la méthode sql-jdbc.conn/connection-details->spec, qui permet au pilote d'établir une connexion avec IRIS JDBC.

(defn- jdbc-spec
	[{:keys [host port namespace user password additional-options]
		:or {host "localhost", port 1972, namespace "USER"}
		:as details}]

	(-> {:classname "com.intersystems.jdbc.IRISDriver"
	:subprotocol "IRIS"
	:subname (str "//" host ":" port "/" namespace)
	:user user
	:password password}
	(merge (dissoc details :host :port :namespace :user :password :additional-options))
	(sql-jdbc.common/handle-additional-options additional-options)))

(defmethod sql-jdbc.conn/connection-details->spec :iris-jdbc
	[_ details-map]
	(jdbc-spec details-map))

Cette méthode fait appel à la méthode d'assistance jdbc-spec qui lit d'abord les données saisies par l'utilisateur avant de créer une chaîne de connexion et de la transmettre à la fonction sql-jdbc appropriée qui se chargera de connecter l'application à l'instance IRIS.

Description des bases de données

Lorsque Metabase se connecte pour la première fois à une base de données, le programme exécute une série de requêtes SQL afin de déterminer, entre autres, les tables auxquelles l'utilisateur a accès ainsi que les métadonnées de ces tables. Cela se fait via un appel à la méthode describe-database, qui appelle ensuite sql-jdbc.sync/have-select-privilege? et sql-jdbc.sync/fallback-metadata-query, entre autres méthodes. Les premières tentatives pour remplacer la méthode globale describe-database n'ont pas vraiment fonctionné, car j'aurais dû implémenter toute la logique de la méthode. Je me suis donc concentré sur les appels de méthode qui échouaient et j'ai simplement décidé de les remplacer.

(defmethod sql-jdbc.sync/fallback-metadata-query :iris-jdbc
	[_ _db-name schema table]

	[(format "SELECT * FROM %s.%s WHERE 1=0 LIMIT 0"
		schema
		table)])

(defmethod sql-jdbc.sync/have-select-privilege? :iris-jdbc
	[_ _db schema table]

	[(format "SELECT 1 AS _ FROM %s.%s WHERE 1=0"
		schema
		table)])

En consultant les journaux Docker, nous avons constaté que Metabase interrogeait les métadonnées par défaut à l'aide d'une balise "zero row probe" qui interrogeait le nom et le type des colonnes d'une table. Cette opération était effectuée à l'aide d'une instruction SQL similaire à celle-ci:

SELECT TRUE FROM "schema"."table" WHERE 1 <> 1 LIMIT 0

Cet appel ne fonctionne pas sur IRIS pour deux raisons : l'utilisation du mot-clé TRUE et 1 <> 1. Cela a été fait dans la méthode describe-database avant l'appel de la méthode fallback-metadata-query et l'échec. La seule solution que j'ai trouvée pour résoudre ce problème était de remplacer la méthode fallback-metadata-query. J'ai donc modifié la requête SQL pour qu'elle puisse être exécutée par le pilote JDBC d'IRIS sur IRIS.

Un appel similaire:

SELECT TRUE AS _ FROM "schema"."table" WHERE 1 <> 1

était utilisé par Metabase pour vérifier si un utilisateur avait les privilèges de sélection pour une table donnée, ce qui échouait pour des raisons similaires

Ce qui est intéressant, c'est que le remplacement de la requête de secours sous-jacente semble être le moyen le plus simple de résoudre ce problème, du moins pour IRIS. Cependant, tous les autres pilotes disponibles dans le référentiel Metabase ont leur propre version de describe-database, ce qui me porte à croire qu'il existe un moyen plus clair et plus efficace d'obtenir le même résultat.

Maintenabilité

IRIS ne supporte pas non plus la maintenabilité au-delà de CLOSE_CURSORS_ON_COMMIT, alors que Metabase s'attend à ce qu'une base de données la supporte par défaut. Bien qu'IRIS prenne en charge la maintenabilité au-delà de HOLD_CURSORS_ON_COMMIT, j'ai décidé de m'inspirer d'autres pilotes confrontés au même problème en désactivant complètement la maintenabilité jusqu'à ce que je trouve un moyen de l'implémenter uniquement au-delà de HOLD_CURSORS_ON_COMMIT.

(defmethod sql-jdbc.execute/statement :iris-jdbc
	[driver ^Connection conn]
	(.createStatement conn))

(defmethod sql-jdbc.execute/prepared-statement :iris-jdbc
	[driver ^Connection conn ^String sql]
	(.prepareStatement conn sql))

Prévention des schémas/tables du système

À sa conception, IRIS est préchargé avec un ensemble de schémas et de tables système qui fournissent des fonctionnalités très utiles, mais qui ne sont pas nécessaires pour l'analyse commerciale. La manière la plus simple d'empêcher la synchronisation des schémas avec Metabase consiste à remplacer la méthode sql-jdbc.sync/excluded-schemas, qui consiste en renvoyant un ensemble de chaînes contenant les noms des schémas que vous souhaitez exclure.

(defmethod sql-jdbc.sync/excluded-schemas :iris-jdbc [_]
   #{"Ens"})

C'est utile, mais IRIS a tout simplement trop de schémas système pour que cela fonctionne dans la pratique. J'ai donc choisi à la place de remplacer la méthode sql-jdbc.sync/filtered-syncable-schemas.

(def ^:private sql-jdbc-default
	(get-method sync.interface/filtered-syncable-schemas :sql-jdbc))

(defmethod sync.interface/filtered-syncable-schemas :iris-jdbc
	[driver ^java.sql.Connection conn ^java.sql.DatabaseMetaData meta
	^String incl-pat ^String excl-pat]

	(let [filtered-schemas
		(into []
			(remove (fn [schema]
				(or
				(str/starts-with? schema "%")
				(str/starts-with? schema "EnsLib_")
				(str/starts-with? schema "Ens_")
				(str/starts-with? schema "EnsPortal")
				(= schema "INFORMATION_SCHEMA")
				(= schema "Ens"))))
			(sql-jdbc-default driver conn meta incl-pat excl-pat))]


	(doseq [schema filtered-schemas]
		(log/infof "[IRIS-DRIVER] Remaining schema → %s" schema))
	
	filtered-schemas))

L'implémentation par défaut de cette méthode consiste à récupérer tous les schémas disponibles et à supprimer ceux qui sont inclus dans l'ensemble renvoyé par la méthode sql-jdbc.sync/excluded-schemas. Le simple remplacement de la méthode m'obligerait à réécrire le code permettant de récupérer tous les schémas disponibles. Pour éviter cela, j'ai défini une méthode privée sql-jdbc-default qui enregistre l'implémentation par défaut de la méthode avant de la remplacer. De cette façon, je peux appeler l'implémentation par défaut dans ma méthode remplacée, ce qui me permet de filtrer les schémas de manière dynamique.

Projets futurs

Mon but final est d'intégrer officiellement ce pilote dans le référentiel principal de Metabase, ce qui le rendrait accessible à l'ensemble de la communauté sans nécessiter de l'installer manuellement. Pour cela, je vais devoir développer une suite de tests complète qui couvre tous les cas limite et garantit que le pilote fonctionne de manière fiable avec différentes versions et configurations d'IRIS. Cela implique d'écrire des tests unitaires pour les mappages de types, des tests d'intégration pour diverses opérations SQL et probablement quelques benchmarks de performances pour s'assurer que nous n'introduisons aucune régression.

Au-delà d'une simple fusion, il existe certainement certains domaines dans lesquels l'implémentation actuelle pourrait être améliorée. Le système de mappage des types, bien que fonctionnel, pourrait être plus nuancé, en particulier en ce qui concerne la manière dont nous traitons les types de données plus spécialisés d'IRIS. Pour ce faire, je devrais examiner l'implémentation JDBC d'IRIS afin de déterminer les mappages exacts entre les types IRIS et Java.

Enfin, je suis convaincu que les implémentations des fonctions arithmétiques DateTime et de date peuvent être améliorées, en commençant par quelques tests visant à déterminer si l'implémentation actuelle fonctionne correctement.

Conclusion

Je vais certainement travailler surtoutes ces améliorations dès que j'aurai un peu de temps libre. La communauté semble très intéressée par une meilleure prise en charge d'IRIS dans Metabase, cela semble donc être un investissement utile. N'hésitez pas à me faire part de vos idées ou à soumettre des pull requests :)

Un grand remerciement à @Martyn Lee et @Bryan Hoon du bureau de Singapour pour m'avoir donné l'opportunité de travailler sur ce projet et m'avoir aidé à résoudre certains problèmes qui se sont présentés au cours du processus.

0
0 27
Article Liam Evans · Août 20, 2025 4m read

Dans le cadre de mon projet stagiaire, je développe une application backend Flask de l'API REST. Mon objectif est de l'héberger sur InterSystems IRIS à l'aide de l'interface WSGI. Il s'agit d'une approche relativement nouvelle qui n'est actuellement utilisée que dans quelques projets tels que AskMe. Pour aider ceux qui souhaitent se lancer, j'ai décidé d'écrire cet article afin de simplifier le processus.

Création d'une application Flask de base

Commençons par créer une application Flask minimale. Voici le code:

0
0 19
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 Lorenzo Scalese · Août 11, 2025 4m read

Mon intention est de montrer à quel point il est simple de générer un tableau de recherche en tenant compte des informations qui arrivent dans notre messagerie HL7. Certes, le tableau de recherche de messages HL7 fourni par IRIS est suffisant pour la plupart des recherches que nous souhaitons effectuer, mais nous avons toujours ce champ spécial de notre HIS, LIS, RIS, etc. dans lequel nous aimerions rechercher. Mais il se trouve dans un segment en dehors de cette table. Ce champ nous oblige à générer une recherche spécifique en utilisant les critères de recherche avancés. Nous aurons certainement beaucoup de messages et nous devrons également filtrer par date et heure pour éviter un délai d'expiration.
 

Comment résoudre ce problème?

En créant notre propre table de recherche.

Et comment créer notre table de recherche?

Comme nous l'avons toujours fait, EN COPIANT! Dans notre vie, nous rencontrerons 3 ou 4 génies, ce seront ceux qui inventeront, créeront, visualiseront l'au-delà, etc. Si vous êtes l'un d'entre eux, vous savez déjà comment faire. Vous serez dans le top 100 du classement mondial, vous aurez plus de 500 000 points et tout un arsenal de produits InterSystems chez vous. Les autres feront comme nous avons fait toutes ces années avant le CHAT GPT, copier sans aucune honte, la tête bien haute. Alors, commençons.

Première étape

Nous allons générer notre classe dans Visual Studio Code, ouvrir la classe EnsLib.HL7.SearchTableque fournie par IRIS , et copier tout le contenu de la classe CTRL+C. Nous allons ensuite dans notre classe et CTRL+V.
 

Très important: nous copierons également les Extends, les ClassType, Inheritance. Nous ne laissons rien de côté, à l'exception du Copyright, qui ne nous intéresse pas.  Nous n'avons pas assez d'ego pour laisser notre empreinte sur un travail remarquable.

Si vous êtes d'ancienne génération, cet écran vous semblera très familier. 😉

Deuxième étape

Maintenant nous faisons opérer la magie: nous ajoutons nos champs, supprimons ceux qui ne nous intéressent pas, et pouvons même les traduire en espagnol pour paraître plus compétents.
 

Dans ce cas, j'ai ajouté la source, la destination, l'événement, le service et le dossier patientau message, et c'est là que vous intervenez. Ajoutez tous les champs que vous souhaitez/devez ajouter. Comme vous le voyez, vous pouvez ajouter le segment, le champ et le composant sous forme numérique. Pour moi, c'est plus facile que d'ajouter le nom en anglais, mais gardez à l'esprit que nous ne pouvons garantir que le segment 1 sera le MSH ; le reste dépendra de chaque message. Donc, dans le cas des segments, il est préférable d'utiliser le code (PID, PV1).

Après tout ce travail fastidieux, nous compilons et avons maintenant notre table de recherche. Il ne nous reste plus qu'à l'attribuer aux composants de notre production, évidemment les composants HL7. Passons à la suite.

Troisième étape

Nous ouvrons notre production et recherchons les composants HL7, nous allons dans les paramètres supplémentaires et nous trouvons notre table de recherche, nous la sélectionnons et procédons à l'application des modifications. À partir de ce moment, tous les messages qui entrent par ce composant seront stockés dans notre table de recherche. Si nous ajoutons ultérieurement d'autres champs à la table de recherche, ils seront enregistrés à partir de la compilation de celle-ci, et pour les messages précédents, ce champ sera vide.

Qu'ils soient entrants ou sortants, dans les opérations de type HL7, nous avons également la table de recherche. Normalement, dans les composants de sortie, rien n'est activé par défaut, mais si nous souhaitons contrôler la sortie, cette option est également disponible.

Grâce à ces étapes simples, notre production est prête à enregistrer les données qui nous intéressent. Il ne nous reste plus qu'à générer les requêtes, et pour cela, nous allons dans la visionneuse de messages.

Démonstration

Dans la messagerie, dans les critères, lorsque nous sélectionnons les tables de recherche, nous retrouvons ce que nous avons fait. Lorsque nous le sélectionnons, nous pouvons maintenant commencer à jouer avec nos champs

Je vais vous donner plusieurs exemples, mais ne limitez pas votre imagination. Je me suis laissé emporter. Les champs du tableau de recherche vous indiqueront les limites.


1. Recherche sans condition. Nous voulons uniquement voir l'événement du message entrant et l'identifiant du message.

2. Recherche par événement affichant les codes du patient et de l'épisode


3. Recherche par épisode montrant le service, le nom du patient et le type de message HL7


À partir de là, c'est à vous de jouer, profitez-en bien 😋

0
0 30
Question Cécile Heuillet · Mai 26, 2025

Bonjour,

je me suis rendue compte que j'avais cette erreur qui est survenue sur une interop qui tournait depuis quelques mois  :

"BP completion cleanup error deleting BP instance Id: 1133, ERREUR #5540: SQLCODE : Message -106 : Échec de suppression de la ligne de la table « xxx.Context » avec  %rowid=« 1012 », la ligne avec cet ID n'a pas été trouvée."

Nous n'avons pas fait de modification à ce moment là et je ne trouve pas d'où vient l'erreur. Nous avions fait plus tôt un changement de namespace mais c'était bien plus tôt.

Et par ailleurs cela ne met pas le système en erreur.

3
0 51
Article Sylvain Guilbaud · Août 5, 2025 9m read

Bonjour, chers membres de notre communauté de développeurs!

Dans l'article d'aujourd'hui, nous allons examiner l'une des dernières fonctionnalités de télésurveillance de nos instances IRIS qui a été ajoutée au produit. Il s'agit de la compatibilité avec OpenTelemetry.

Que qu'est-ce que OpenTelemetry?

OpenTelemetry est un framework open source qui fournit les outils nécessaires, tels que des SDK et des normes, pour mettre en œuvre l'observabilité dans les systèmes et les applications.

Cette observabilité s'étend à trois types de données:

  1. Traces : contrôle du flux d'informations circulant dans les solutions grâce à l'inclusion de traces permettant d'identifier où elles passent et sous quelles conditions.
  2. Métriques : état du système, performances, latences dans les réponses, utilisation des ressources, etc.
  3. Journaux : inclus dans les systèmes pour une meilleure compréhension des événements qui se produisent.

OpenTelemetry utilise la norme ouverte OTLP qui définit comment toute la télémétrie précédemment définie doit être sérialisée et transportée. L'envoi de cette télémétrie peut être effectué via HTTP ou gRPC.

Open Telemetry avec IRIS

InterSystems IRIS exploite les fonctionnalités disponibles via OpenTelemetry SDK pour permettre l'exportation de toutes les données de télémétrie générées par l'instance configurée. D'où proviennent ces données de télémétrie? 

  1. Métriques : elles proviennent des informations dont nous disposons via l'API REST /api/monitor ( vous pouvez consulter la documentation officielle de cette API ici ).
  2. Journaux : messages enregistrés dans le fichier messages.log et informations stockées dans la base de données d'audit (si elle est activée).
  3. Traces : traces définies par l'utilisateur dans les applications développées dans l'instance.

Voyons maintenant comment configurer OpenTelemetry dans une instance IRIS

Configuration d'IRIS avec OpenTelemetry

Pour notre exemple de configuration, j'ai utilisé un projet que j'ai téléchargé sur GitHub et qui fonctionne sous Docker, mais il ne serait pas très compliqué de le configurer sur une instance installée sur site. Ce projet est disponible sur OpenExchange, associé à cet article.

Avant de présenter les différentes configurations, nous allons expliquer quels éléments feront partie de notre "écosystème de surveillance":

InterSystems IRIS

L'instance IRIS se chargera de générer les données de télémétrie que nous devons surveiller.

OpenTelemetry Collector

Il s'agit d'un outil fourni par OpenTelemetry chargé de collecter des données télémétriques provenant de différentes sources. Dans notre exemple, il s'agira uniquement d'IRIS, mais nous pourrions en ajouter autant que nécessaire.

Prometheus

Outil open source utilisé pour la surveillance des systèmes et la génération d'alertes. Cet outil sera chargé de recevoir les métriques accumulées par OpenTelemetry Collector.

Jaeger

Plateforme open source pour la gestion et la surveillance des traces des systèmes basés sur des microservices.

Configuration en Docker

Comme je l'ai mentionné précédemment, j'ai utilisé un déploiement en Docker afin de simplifier au maximum l'exemple. Analysons le fichier docker-compose.yml par parties pour mieux le comprendre.

Image d'IRIS Docker

  iris:    init:true    container_name:iris    build:      context:.      dockerfile:iris/Dockerfile    ports:      -52774:52773      -51774:1972    volumes:    -./iris/shared:/iris-shared    environment:    -ISC_DATA_DIRECTORY=/iris-shared/durable    -OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318    command:--check-capsfalse--ISCAgentfalse

Comme vous pouvez le voir, je définis l'image à utiliser dans un fichier Dockerfile qui ne présente pas grand intérêt et je ne vais donc pas m'attarder dessus. Le point intéressant de cette configuration est la définition d'une variable d'environnement appelée  OTEL_EXPORTER_OTLP_ENDPOINT  , qui sera l'URL où notre OpenTelemetry Collector attendra qu'IRIS lui envoie toutes les métriques, traces et journaux dont il dispose.

Une fois que nous aurons déployé notre instance IRIS, nous devrons la configurer pour qu'elle émette régulièrement les métriques et les journaux. Pour ce faire, nous accéderons à la configuration du moniteur depuis le portail de gestion:

Comme vous pouvez le voir, nous pouvons activer l'envoi des métriques et des journaux. Dans le cas des traces, celles-ci ne sont pas envoyées à intervalles réguliers, mais dès que la méthode "End" de l'instance de la classe %Trace.Provider est invoquée. Je n'entrerai pas plus dans les détails, mais vous pouvez consulter  the official documentation ici la documentation officielle.

Image d'OpenTelemetry Collector

otel-collector:
    build:
      context: .
      dockerfile: open-telemetry/Dockerfile
    container_name: otel-collector
    command: ["--config=/otel-local-config.yml"]
    volumes:
      - ./open-telemetry/otel-collector-config.yml:/otel-local-config.yml
    ports:
      - 4317:4317      # OTLP gRPC receiver
      - 4318:4318    # OTLP HTTP receiver
      - 9464:9464      # Metrics
    depends_on:
      - iris

Voici l'image d'OpenTelemetry Collector. J'ai à nouveau défini un fichier Dockerfile pour indiquer où obtenir l'image (ce n'est pas obligatoire). Comme vous pouvez le voir, nous rendons trois ports accessibles:

  • 4317: port pour la réception des métriques, des traces et des journaux via gRPC.
  • 4318: port pour la réception des métriques, des traces et des journaux via HTTP.
  • 9464: port ouvert pour que des outils tiers puissent consulter les informations reçues par OpenTelemetry Collector.

Nous avons également déclaré un fichier de configuration,  otel-local-config.yml  (le nom est modifiable). Jetons un œil à son contenu:

receivers:  otlp:    protocols:      grpc:        endpoint:"0.0.0.0:4317"      http:        endpoint:"0.0.0.0:4318"exporters:  otlp:    endpoint:jaeger:4317    tls:      insecure:true  prometheus:    endpoint:"0.0.0.0:9464"  debug:{}service:  pipelines:    traces:      receivers:[otlp]      exporters:[otlp]    metrics:      receivers:[otlp]      exporters:[prometheus]    logs:      receivers:[otlp]      exporters:[debug]

Que voyons-nous ici? C'est très simple, nous avons les sections suivantes: 

  • Récepteurs de données  , que nous avons appelés otlp dans notre cas, dans lesquels nous configurons l'adresse IP et les ports sur lesquels notre récepteur va attendre les informations provenant de systèmes tiers.
  • Exporteurs de données : où nous allons envoyer ou qui va extraire les données reçues dans l'OpenTelemetry Collector. Pour notre exemple, nous avons utilisé un exporteur déjà inclus dans OpenTelemetry, à savoir, Prometheus, which  qui se chargera d'obtenir les métriques dans l'instance locale de l'OpenTelemetry Collector sur le port 9464. Dans le cas de Jaeger , nous utilisons directement la capacité d'OpenTelemetry à envoyer des données directement à une IP (jaeger est le nom de l'instance au sein du réseau monté par Docker) qui correspondra à notre instance jaeger.
  • Services , où nous indiquerons lesquels des composants que nous avons configurés comme récepteurs et exporteurs se chargeront de la réception et de l'émission des métriques, des traces et des journaux. Comme vous pouvez le voir dans notre cas OpenTelemetry Collector sera le récepteur que nous utiliserons pour tout, Prometheus sera le récepteur des métriques et Jaeger, via OpenTelemetry Collector, sera le récepteur des traces.

Image de Prometheus Docker

Let's take a look at its configuration in Docker:

prometheus:    build:      context:.      dockerfile:prometheus/Dockerfile    container_name:prometheus    volumes:      -./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml    ports:      -9090:9090

Comme vous pouvez le constater, c'est très simple: notre image est à nouveau définie dans un fichier Dockerfile qui fait uniquement référence au nom de l'image, à un port 9090 où sera déployée l'interface web à laquelle nous pourrons accéder, et enfin à un fichier de configuration appelé  prometheus.yml 

Voyons ce que nous dit ce fichier prometheus.yml:

global:  scrape_interval:15sscrape_configs:  - job_name:'otel-collector'    static_configs:      - targets:['otel-collector:9464']

Nous avons la configuration suivante:

  • Scrape interval : intervalle de temps entre deux requêtes à OpenTelemetry Collector.
  • Scrape configs : lieu où nous donnons un nom à la tâche qui effectuera la recherche, ainsi que l'adresse IP et le port auxquels elle se connectera pour cette recherche.

Image de Jaeger Docker

Pour l'image de Jaeger, j'ai directement repris un exemple disponible sur notre cher chatGPT:

jaeger:
    image: jaegertracing/all-in-one:latest
    container_name: jaeger
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    ports:
      - 16686:16686    # Jaeger UI
      - 14250:14250    # OTLP gRPC receiver

Le point le plus important, outre le port 16689 que nous utiliserons pour accéder à l'interface web, est la variable d'environnement COLLECTOR_OTLP_ENABLED, qui activera les ports par défaut de Jaeger pour permettre les connexions HTTP depuis OpenTelemetry Collector.  Ici  vous pouvez voir la liste des ports utilisés, mais je vous informe que le port 4317 est utilisé pour la transmission gRCP et le port 4318 pour le HTTP. Comme vous l'avez vu dans la configuration d'OpenTelemetry Collector, nous allons utiliser la connexion via gRCP.

Maintenant que tout est prêt, voyons comment cela fonctionne en démarrant le projet.

Émission des métriques, réception dans Prometheus

Nos instances sont désormais configurées et démarrées. Accédons à l'interface web fournie par Prometheus.  Dans notre exemple, nous l'avons mappée à http://localhost:9090 . Par défaut, elle nous indique où nous pouvons lancer des requêtes sur les métriques disponibles.

Si nous avons correctement configuré la connexion entre IRIS - OpenTelemetry Collector - Prometheus, depuis l'écran de requêtes de Prometheus, nous aurons accès à toutes les métriques standard disponibles dans IRIS, comme vous pouvez le voir dans la capture d'écran suivante en recherchant "iris_"

Si nous sélectionnons l'une d'entre elles, nous pouvons voir un graphique au fil du temps

Envoi de traces à Jaeger

Pour vérifier le fonctionnement de l'envoi des traces, nous allons utiliser la ressource la plus simple mise à notre disposition par IRIS, à savoir la méthode TestTraces() de la classe SYS.Monitor.OTel que vous pouvez consulter ici . Si vous souhaitez obtenir plus de détails, n'hésitez pas à me le faire savoir dans les commentaires et je me ferai un plaisir de rédiger un article à ce sujet.

Nous se limitons à exécuter la commande depuis la console à partir de l'espace de noms SYS:

%SYS>do##class(SYS.Monitor.OTel).TestTraces()

Cela nous renverra une trace qui devrait avoir été reçue par Jaeger. Vérifions cela depuis son interface graphique affichée sur http://localhost:1668

Dans les filtres du menu de gauche, nous pouvons voir que nous avons un service appelé  irisotel , Ce service est utilisé par IRIS pour tester l'envoi de traces de test, d'où le nom de la trace reçue test_trace .

Et voilà! Notre instance est désormais prête à envoyer toutes les données métriques et traces que nous souhaitons. Je reste à votre disposition si vous souhaitez approfondir le sujet.

0
0 29
Article Benjamin De Boe · Juil 30, 2025 13m read

Cet article décrit une amélioration significative apportée dans la version 2025.2 à la manière dont InterSystems IRIS traite les statistiques de table, un élément crucial pour le traitement SQL IRIS. Nous commencerons par un bref rappel sur ce que sont les statistiques de table, comment elles sont utilisées et pourquoi cette amélioration était nécessaire. Nous nous intéresserons ensuite en détail à la nouvelle infrastructure de collecte et d'enregistrement des statistiques de table, puis nous examinerons ce que ce changement signifie en pratique pour vos applications. Nous terminerons par quelques remarques supplémentaires sur les modèles rendus possibles par le nouveau modèle et nous attendons avec impatience les étapes suivantes de cette première livraison.

Je n'utilise pas SQL, pourquoi suis-je ici? – C'est une question existentielle pertinente (wink) , mais cet article peut néanmoins contenir des informations précieuses pour vous. Certaines fonctionnalités avancées d'IRIS, notamment Interopérabilité, utilisent IRIS SQL en arrière-plan, et certains changements de comportement peuvent vous inciter à activer une nouvelle tâche de maintenance. Reportez-vous à la section "Collecte automatique des statistiques des tables" ci-dessous.

Statistiques de table 101

SQL signifie Structured Query Language (langage de requête structuré). SQL est un langage dans lequel vous exprimez ce que vous voulez (la requête), plutôt que comment vous le voulez (le code permettant d'obtenir le résultat de la requête). C'est au moteur de requête d'une base de données qu'il revient d'examiner toutes les options et de choisir le plan vraisemblablement optimal pour récupérer le résultat que vous demandez. Pour déterminer le meilleur plan, l'optimiseur de requêtes a essentiellement besoin de deux choses: des informations sur la structure de votre table, telles que l'emplacement de stockage de vos données et les index disponibles, et des informations sur les données de la table elle-même: les statistiques de table. Ces statistiques de table couvrent des informations telles que la taille estimée de la table et la sélectivité de chaque champ, qui exprime la proportion moyenne de la table correspondant à une valeur de champ particulière. Ces informations sont essentielles pour prendre les bonnes décisions concernant les plans de requête. Supposons que vous deviez trouver le nombre d'utilisateurs féminins dans un code postal particulier et que vous puissiez choisir entre partir d'un index sur le sexe ou sur le code postal. La sélectivité de l'index sur le sexe sera d'environ 50 %, tandis que celle sur le code postal pourra être aussi faible que 1 % ou moins, ce qui signifie que vous filtrerez beaucoup plus rapidement l'ensemble des lignes à rechercher en partant de ce dernier. 

Si la structure des tables fait naturellement partie de votre application et peut être intégrée au code de celle-ci, ce n'est pas nécessairement le cas des statistiques de table. Si l'exemple précédent est tiré d'une petite application librairie, il est probable que les valeurs de sélectivité des champs soient valables pour toutes les librairies dans lesquelles l'application est déployée, mais s'il s'agit d'un système de dossiers médicaux électroniques, la sélectivité du champ sexe sera différente entre une maternité et un cabinet médical généraliste, et le nombre de codes postaux (et donc leur sélectivité) dépendra de la taille de la zone couverte par l'hôpital. Il est évident que vous souhaitez que vos statistiques, et donc vos plans de requête, soient basés sur les données réelles de l'environnement dans lequel l'application est déployée.

Statistiques de table dans IRIS - avant 2025.2

Sur IRIS, les statistiques de table ont toujours été stockées dans la définition d'une classe. Cela présente plusieurs inconvénients

  • Comme décrit précédemment, les distributions de données peuvent varier considérablement entre les environnements dans lesquels vous déployez votre application. Ceci n'est pas seulement vrai pour la taille estimée des tables et la sélectivité, mais encore plus pour d'autres types de statistiques plus avancées telles que les informations sur les valeurs aberrantes et les histogrammes.
  • Lorsque vous déployez une version mise à jour de votre application, vous souhaitez généralement conserver toutes les statistiques existantes, en supposant qu'elles sont basées sur les données réelles de cet environnement. Cela s'avère plus délicat lorsqu'elles sont stockées en tant que partie intégrante du code, car vous ne souhaitez évidemment pas déployer accidentellement les statistiques de table de votre environnement de développement ou de test sur le système d'un utilisateur.
  • Lorsque le code d'application est déployé sous la forme d'une base de données en lecture seule ou lorsqu'il fait partie d'une bibliothèque système IRIS (comme les messages d'interopérabilité), il n'existe aucun moyen efficace de mettre à jour les statistiques de ces tables.

Consultez également cet article précédent qui fournit davantage de détails à ce sujet. Voici certaines raisons qui nous ont poussés à repenser la gestion des statistiques de table dans IRIS. La nouvelle infrastructure est désormais incluse dans InterSystems IRIS 2025.2.

Quel est le changement?

Statistiques collectées vs statistiques fixes

Comme décrit dans les exemples précédents, le stockage des statistiques dans le code de l'application n'a de sens que dans des environnements très spécifiques et contrôlés. Dans la plupart des cas, il est préférable de conserver les statistiques avec les données à partir desquelles elles ont été collectées, plutôt que de les fixer dans le code. C'est donc ce que nous faisons dans le nouveau modèle. Nous faisons la distinction entre les statistiques collectées qui sont toujours basées sur des données réelles et stockées dans l'index d'extension (une variable globale contenant d'autres détails au format registre sur les données de votre table, également appelées extensions), et les statistiques fixes qui sont codées en dur dans la définition de classe par le développeur de l'application et peuvent être basées sur des données réelles ou sur des hypothèses raisonnables. Comme vous pouvez le deviner, les statistiques fixes correspondent au modèle antérieur à la version 2025.2 pour la gestion des statistiques de table. 

Utilisez la nouvelle commande COLLECT STATISTICS FOR TABLE t   (qui est 100 % synonyme de la commande existante TUNE TABLE , (qui est 100 % synonyme de la commande existante TUNE TABLE, et qui sert simplement à normaliser la terminologie) pour remplir un nouvel ensemble de statistiques basé sur les données actuelles de votre table. Les statistiques de table sont très petites, nous n'écrasons donc pas les statistiques précédentes, mais stockons plutôt un nouvel ensemble et conservons les anciennes statistiques à des fins d'information jusqu'à ce qu'elles répondent aux critères de purge par défaut ou soient explicitement purgées. Si vous souhaitez passer au modèle fixe, vous pouvez enregistrer les statistiques collectées dans la définition de classe à l'aide de ALTER TABLE t FIX STATISTICS. Cela peut faire partie de la procédure de package de votre application, si vous êtes certain que ces statistiques correspondent à votre environnement cible et que vous préférez le modèle statique.

Par défaut, les statistiques fixes prévalent sur les statistiques collectées. Cela garantit la rétrocompatibilité et signifie qu'en tant que développeur, c'est toujours vous qui avez le dernier mot. En fait, si vous avez des statistiques fixes, même pour un seul champ, les statistiques collectées seront ignorées et nous reviendrons à des estimations sûres basées sur le type de données du champ pour tous les champs qui n'ont pas de statistiques fixes. Si vous souhaitez tester les statistiques collectées sans supprimer vos statistiques fixes, vous pouvez inclure l'indication %NOFIXEDSTATS hint dans la clause FROM de votre texte de requête (par table) afin d'obtenir le plan de requête basé uniquement sur les statistiques collectées, ou utiliser ALTER TABLE t SET RUNTIME IGNOREFIXEDSTATS = TRUE pour définir ce comportement au niveau de la table. 

Collecte automatique des statistiques de table

L'introduction des statistiques collectées résout déjà bon nombre des inconvénients liés au packagage et au déploiement mentionnés dans l'introduction. Cependant, elle ne résout pas vraiment l'un des problèmes les plus courants rencontrés par les utilisateurs: le manque de statistiques à jour . Comme décrit dans l'introduction, des statistiques précises sont essentielles pour obtenir les meilleurs plans de requête pour votre distribution de données spécifique. Si vous ne vous êtes jamais soucié de les collecter, l'optimiseur utilisera des valeurs par défaut approximatives basées sur le type de données de vos champs, mais celles-ci sont peu susceptibles de tenir compte correctement des spécificités de votre application dans tous les cas. À partir de la version 2021.2, IRIS collectera automatiquement les statistiques pour les tables qui n'en ont pas et qui sont éligibles à une technique d'échantillonnage rapide au moment de la requête, mais cela ne se produira qu'une seule fois, et peut-être à un moment où votre table n'aura pas encore été remplie avec des données représentatives. Mais même lorsque les statistiques ont été collectées à un moment opportun, les distributions de données évoluent avec le temps (notamment les histogrammes) et nous constatons très souvent que les utilisateurs obtiennent des plans de requête sous-optimaux simplement parce qu'ils sont basés sur des statistiques obsolètes.

Avec la version 2025.2, nous introduisons une nouvelle tâche système qui s'exécute pendant une fenêtre de maintenance de nuit et qui, pour chaque espace de noms, prend en compte toutes les tables qui n'ont aucune statistique, pour lesquelles les statistiques les plus récentes ont été invalidées (par exemple après avoir modifié la définition de stockage de la table et recompilé) ou pour lesquelles au moins 25 % des données de la table ont changé depuis la dernière collecte de statistiques (environ, ceci est mesuré sur la base du ROWCOUNT combiné pour les opérations DML sur cette table). Les statistiques sont ensuite collectées pour ces tables une par une, ou jusqu'à ce que la durée maximale de la tâche de maintenance (configurable) soit atteinte. D'autres options de configuration permettent de choisir de prendre en compte les tables avec des statistiques fixes (ON par défaut), les tables mappées vers un stockage distant (OFF par défaut) et les tables qui ne sont pas éligibles pour une technique d'échantillonnage rapide (OFF par défaut). Si nécessaire, vous pouvez marquer des tables individuelles à l'aide d'un paramètre de classe SKIPAUTOMATICSTATSCOLLECTION  (ou d'un indicateur d'exécution équivalent) afin qu'elles soient ignorées par cette tâche système (notez que ce nouvel indicateur affecte également le comportement AutoTune préexistant).

Dans la version 2025.2, cette tâche système est désactivée par défaut, car nous souhaitons évaluer avec les utilisateurs l'impact de cette fonctionnalité sur les modèles d'E/S des systèmes réels. Nous encourageons les utilisateurs à activer la tâche système et à nous communiquer leur expérience en matière de charge système afin que nous puissions procéder aux ajustements nécessaires avant de l'activer par défaut dans une version ultérieure. À ce moment-là, nous prévoyons de supprimer le fonctionnement précédent d' AutoTune qui pouvait se déclencher pendant le traitement des requêtes.

La collecte manuelle des statistiques de table est bien sûr toujours prise en charge, et l'exécution de COLLECT STATISTICS après des chargements ou des purges de données importants reste une bonne pratique.

Qu'est-ce que cela signifie pour votre application?

Nous avons pris soin de concevoir le nouveau modèle afin que les applications SQL mises à niveau vers IRIS 2025.2 ne subissent aucun changement. Les statistiques préexistantes seront désormais traitées comme des statistiques fixes, qui auront priorité sur toutes les statistiques collectées automatiquement en arrière-plan. Nous conseillons aux utilisateurs qui passent au nouveau modèle de tenir compte des trois recommandations suivantes:

Suppression des statistiques fixes après la mise à niveau

Si votre application ou, en fait, n'importe quelle table avait des statistiques avant la mise à niveau (ce qui serait le cas pour toutes les tables si vous avez suivi les meilleures pratiques), envisagez de les supprimer purement et simplement. Après la mise à niveau, elles seront considérées comme des statistiques fixes et auront priorité sur toutes les statistiques que vous collecterez explicitement (peut-être via des scripts personnalisés appelant TUNE TABLE) ou implicitement. Les statistiques fixes des tables peuvent être supprimées à l'aide d'une nouvelle commande ALTER TABLE t DROP FIXED STATISTICS ou de la commande équivalente ALTER SCHEMA s DROP FIXED STATISTICS. Vous pouvez utiliser l'indication %NOFIXEDSTATS pour vérifier à l'avance l'impact sur les requêtes individuelles, selon vos préférences.

On ne conservera les statistiques fixes que si l'on estime que les statistiques prédéfinies doivent rester inchangées pour maintenir les plans de requête actuels et qu'aucune modification de la distribution des données n'est prévue.

Prise en compte des statistiques pour les classes système

Il convient de noter que le nouveau modèle de statistiques collectées s'applique aux tables dont la définition réside dans une base de données en lecture seule, y compris les tables système IRIS telles que  Ens.MessageHeader. Pour ces tables, les statistiques de table étaient auparavant peu pratiques, voire impossibles à maintenir, mais elles deviendront désormais pertinentes et, lorsque vous aurez activé la tâche du système de collecte, elles seront maintenues automatiquement. Lorsque vous utilisez des productions d'interopérabilité ou d'autres éléments de l'infrastructure IRIS pouvant impliquer SQL en arrière-plan, nous vous recommandons de surveiller les performances des opérations de recherche et autres opérations similaires après la mise à niveau, et de collecter les statistiques manuellement ou d'activer la tâche système. 

Lors de l'utilisation de la recherche de messages, nous avons constaté des améliorations significatives des performances des requêtes après l'adoption du nouveau modèle, car celui-ci pouvait désormais utiliser une sélectivité et d'autres statistiques beaucoup plus réalistes, alors qu'auparavant, il s'appuyait sur des valeurs par défaut approximatives. Nous avons donc supprimé les statistiques fixes fournies avec ces tables système.

Soyez vigilant lors de l'importation et de l'exportation entre différentes versions

Lorsque vous exportez des définitions de table avec l'indicateur /exportselectivity=1 (par défaut), les statistiques sont incluses dans un nouveau format qui prend en charge à la fois les statistiques fixes et les statistiques les plus récentes collectées collected mais qui est incompatible avec les versions antérieures. Pour prendre en charge l'importation dans des instances IRIS exécutant des versions antérieures, utilisez /exportversion=2025.1 ou /exportselectivity=0 selon le cas. Veuillez noter que les statistiques collectées et exportées à l'aide de ce format seront toujours importées en tant que statistiques collectées et ne deviendront pas fixes de manière silencieuse parce qu'elles ont été incluses dans le fichier de définition de classe export . Compte tenu de ces nuances, vous pouvez revoir votre stratégie de contrôle de source afin de vous assurer que les informations correctes sont suivies pour votre modèle de déploiement. Notez également l'indicateur symétrique /importselectivity qui peut être utilisé lors de l'importation de définitions de classe. 

Les méthodes Import() et Export() de la classe $SYSTEM.SQL.Stats.Table ont également été étendues avec un argument de type supplémentaire afin de différencier correctement les deux types de statistiques. Pour plus d'informations, consultez la référence de classe .

Travaux futurs

Cette version comprend toute l'infrastructure nécessaire pour tirer parti du nouveau modèle. Outre quelques améliorations mineures visant à faciliter l'utilisation, nous prévoyons les deux mises à jour suivantes dans une prochaine version (probablement 2025.3)

Activation de la tâche de collecte automatique

Comme indiqué précédemment, nous avons introduit une nouvelle tâche système pour la collecte automatique des statistiques des tables pendant une fenêtre de traitement par lots de nuit, mais nous l'avons désactivée afin d'éviter toute charge E/S inattendue après la mise à niveau. Dans une prochaine version, nous l'activerons par défaut, si elle n'est pas déjà activée par l'utilisateur à ce moment-là.

Taille d'échantillon plus intelligente

Nous avons remarqué que notre algorithme d'échantillonnage rapide par blocs pouvait dans certains cas surestimer la sélectivité des champs hautement sélectifs dans les grandes tables, ce qui signifie que l'optimiseur pouvait ne pas utiliser les index correspondants alors que cela aurait été optimal. Nous mettons en place un algorithme qui affinera de manière dynamique la taille de l'échantillon si nous détectons trop de variabilité entre les échantillonnages. Ce changement n'a pratiquement aucun impact sur la modification globale de l'infrastructure décrite plus haut dans cet article.

Partagez votre expérience

Cela fait longtemps que nous attendions avec impatience le déploiement de cette modification, et nous sommes ravis qu'elle soit désormais incluse dans IRIS 2025.2. Nous avons pris soin de tester cette modification dans de nombreuses configurations différentes et nous sommes convaincus que la nouvelle infrastructure est robuste, mais nous sommes conscients que les modifications peuvent affecter des pratiques de déploiement hautement personnalisées, notamment le mappage des espaces de noms et le packagage du code. Nous vous invitons donc à nous faire part de vos commentaires et espérons que la nouvelle infrastructure vous facilitera la vie.

0
0 21
Article Lorenzo Scalese · Juil 28, 2025 17m read

Découvrez comment concevoir des agents IA évolutifs et autonomes qui combinent raisonnement, recherche vectorielle et intégration d'outils à l'aide de LangGraph.

cover

C'est trop long, vous n'avez pas lu

  • Les agents IA sont des systèmes proactifs qui combinent mémoire, contexte et initiative pour automatiser des tâches dépassant le simple champ d'action des chatbots.
  • LangGraph est un framework qui nous permet de créer des workflows d'IA complexes, en utilisant des nœuds (tâches) et des arêtes (connexions) avec une gestion d'état intégrée.
  • Ce guide vous explique comment créer un agent de support client basé sur l'IA qui classe les priorités, identifie les sujets pertinents et détermine si une escalade ou une réponse automatique est nécessaire.

Alors, les agents IA, c'est quoi exactement?

Soyons réalistes, le terme "agents IA" peut faire penser à des robots qui vont envahir votre bureau. En réalité, il s'agit de vos assistants proactifs qui peuvent rationaliser des flux de travail complexes et éliminer les tâches répétitives. Considérez-les comme la prochaine étape évolutive après les chatbots: ils ne se contentent pas d'attendre des instructions, ils lancent des actions, coordonnent plusieurs étapes et s'adaptent tout au long du processus.

Autrefois, créer un système "intelligent" signifiait jongler avec différents modèles pour la compréhension du langage, la génération de code, la recherche de données, etc., puis les assembler à la va-vite. Auparavant, vous passiez la moitié de votre temps dans l'enfer de l'intégration, et l'autre moitié à corriger les erreurs.

Les agents renversent la tendance. Ils regroupent le contexte, l'initiative et l'adaptabilité en un seul flux orchestré. Il ne s'agit pas seulement d'automatisation, mais d'intelligence au service d'une mission. Et grâce à des frameworks tels que LangGraph, la création de votre propre équipe d'agents peut même devenir... oserais-je dire, amusante?

image

LangGraph, c'est quoi exactement?

LangGraph est un cadre innovant qui révolutionne la manière dont nous construisons des applications complexes impliquant des modèles linguistiques à grande échelle (LLM).

Imaginez que vous dirigez un orchestre: chaque instrument (ou "nœud") doit savoir quand entrer, à quel volume jouer et dans quel ordre. LangGraph, dans ce cas**,** est votre baguette, qui vous donne les informations suivantes:

  • Structure graphique: utilise une structure graphique avec des nœuds et des arêtes, permettant aux développeurs de concevoir des flux de travail flexibles et non linéaires qui s'adaptent aux branches et aux boucles. Elle reflète les processus décisionnels complexes qui ressemblent au fonctionnement des voies neuronales.
  • Gestion d'état: LangGraph offre des outils intégrés pour la persistance d'état et la récupération d'erreurs, simplifiant la gestion des données contextuelles à différentes étapes d'une application. Il peut basculer efficacement entre la mémoire à court terme et la mémoire à long terme, améliorant ainsi la qualité des interactions grâce à des outils tels que Zep.
  • Intégration d'outils: Avec LangGraph, les agents LLM peuvent facilement collaborer avec des services ou des bases de données externes pour récupérer des données du monde réel, améliorant ainsi la fonctionnalité et la réactivité de vos applications.
  • Human-in-the-Loop: Au-delà de l'automatisation, LangGraph s'adapte aux interventions humaines dans les flux de travail, qui sont essentielles pour les processus décisionnels nécessitant une supervision analytique ou une réflexion éthique.

Lorsque vous créez un chatbot doté d'une mémoire réelle, un moteur d'histoires interactives ou une équipe d'agents chargés de résoudre un problème complexe, LangGraph transforme les tâches fastidieuses en une machine à états simple et visuelle.

Pour commencer

Pour commencer à utiliser LangGraph, vous aurez besoin d'une configuration de base qui implique généralement l'installation de bibliothèques essentielles telles que langgraph et langchain-openai. Ensuite, vous pourrez définir les nœuds (tâches) et les bords (connexions) au sein du graphe, en mettant efficacement en œuvre des points de contrôle pour la mémoire à court terme et en utilisant Zep pour les besoins en mémoire plus persistants.

Lorsque vous utilisez LangGraph, gardez à l'esprit les remarques suivantes:

  • Flexibilité de conception: Tirez parti de la puissante structure graphique pour prendre en compte les ramifications et les interactions potentielles du flux de travail qui ne sont pas strictement linéaires.
  • Interaction prudente avec les outils: Améliorez les capacités du LLM à l'aide d'outils externes, mais ne les remplacez pas. Fournissez à chaque outil des descriptions complètes pour permettre une utilisation précise.
  • Utilisation de solutions de mémoire riches: Utilisez la mémoire de manière efficace, soyez attentif à la fenêtre contextuelle du LLM et envisagez d'intégrer des solutions externes pour la gestion automatiquedu contenu factuel.

Nous avons abordé les bases de LangGraph, passons maintenant à un exemple pratique. Pour cela, nous allons développer un agent IA spécialement conçu pour le support client.

Cet agent recevra les demandes par e-mail, analysera la description du problème dans le corps du message, puis déterminera la priorité de la demande et le sujet/la catégorie/le secteur approprié.

Alors, attachez vos ceintures et c'est parti!

buckle up

Pour commencer, nous devons définir ce qu'est un "outil". Vous pouvez le considérer comme un "assistant manager" spécialisé pour votre agent, lui permettant d'interagir avec des fonctionnalités externes.

Le décorateur @tool est ici essentiel. LangChain simplifie la création d'outils personnalisés, ce qui signifie que vous définissez d'abord une fonction Python, puis appliquez le décorateur @tool.

tools

Illustrons cela en créant notre premier outil. Cet outil aidera l'agent à classer la priorité d'un ticket d'assistance informatique en fonction du contenu de l'e-mail:

    from langchain_core.tools import tool
    
    @tool
    def classify_priority(email_body: str) -> str:
        """Classify the priority of an IT support ticket based on email content."""
        prompt = ChatPromptTemplate.from_template(
            """Analyze this IT support email and classify its priority as High, Medium, or Low.
            
            High: System outages, security breaches, critical business functions down
            Medium: Non-critical issues affecting productivity, software problems
            Low: General questions, requests, minor issues
            
            Email: {email}
            
            Respond with only: High, Medium, or Low"""
        )
        chain = prompt | llm
        response = chain.invoke({"email": email_body})
        return response.content.strip()

Excellent! Nous avons maintenant une invite qui demande à l'IA de recevoir le corps de l'e-mail, de l'analyser et de classer sa priorité comme Élevée, Moyenne ou Faible.

C'est tout! Vous venez de créer un outil accessible à votre agent!

Créons maintenant un outil similaire pour identifier le sujet principal (ou la catégorie) de la demande de support:


    @tool
    def identify_topic(email_body: str) -> str:
        """Identify the main topic/category of the IT support request."""
        prompt = ChatPromptTemplate.from_template(
            """Analyze this IT support email and identify the main topic category.
            
            Categories: password_reset, vpn, software_request, hardware, email, network, printer, other
            
            Email: {email}
            
            Respond with only the category name (lowercase with underscores)."""
        )
        chain = prompt | llm
        response = chain.invoke({"email": email_body})
        return response.content.strip()

Nous devons maintenant créer un état, et dans LangGraph, cette petite partie est plutôt importante.

Considérez-le comme le système nerveux central de votre graphe. C'est ainsi que les nœuds communiquent entre eux, en se passant des petits mots comme des surdoués à l'école.

Selon la documentation:

“Un état est une structure de données partagée qui représente l'instantané actuel de votre application.”

Et en pratique? L'état est un message structuré qui circule entre les nœuds. Il transporte le résultat d'une étape comme entrée pour la suivante. En gros, c'est le ciment qui maintient l'ensemble de votre flux de travail.

Par conséquent, avant de construire le graphique, nous devons d'abord définir la structure de notre état. Dans cet exemple, notre état sera composé des éléments suivants:

  • La demande de l'utilisateur (corps de l'email)
  • La priorité attribuée
  • Le sujet identifié (catégorie)

C'est simple et clair, vous pouvez donc vous déplacer à travers le graphe comme un pro.

    from typing import TypedDict

    # Définition de la structure d'état
    class TicketState(TypedDict):
        email_body: str
        priority: str
        topic: str
        
    
    # Initialisation d'état
    initial_state = TicketState(
        email_body=email_body,
        priority="",
        topic=""
    )

Nœuds et bords: Composants clés de LangGraph

Les éléments fondamentaux de LangGraph sont les nœuds et les bords.

  • Nœuds: Il s'agit des unités opérationnelles du graphe qui effectuent le travail proprement dit. Un nœud se compose généralement d'un code Python capable d'exécuter n'importe quelle logique, qu'il s'agisse de calculs ou d'interactions avec des modèles linguistiques (LLM) ou des intégrations externes. Les nœuds sont essentiellement similaires aux fonctions ou agents individuels de la programmation traditionnelle.
  • Bords: les bords définissent le flux d'exécution entre les nœuds et déterminent ce qui se passe ensuite. Elles agissent comme des connecteurs qui permettent à l'état de passer d'un nœud à l'autre en fonction de conditions prédéfinies. Dans le contexte de LangGraph, les bords sont essentiels pour orchestrer la séquence et le flux de décision entre les nœuds.

Pour comprendre le fonctionnement des bords, prenons l'exemple simple d'une application de messagerie:

  • Nœuds sont similaires aux utilisateurs (ou à leurs appareils) qui participent activement à une conversation.
  • Bords symbolisent les fils de discussion ou les connexions entre les utilisateurs qui facilitent la communication.

Lorsqu'un utilisateur sélectionne un fil de discussion pour envoyer un message, un bord est créée, le reliant à un autre utilisateur. Chaque interaction, qu'il s'agisse d'envoyer un message textuel, vocal ou vidéo, suit une séquence prédéfinie, comparable au schéma structuré de l'état de LangGraph. Cela garantit l'uniformité et l'interprétabilité des données transmises le long des bords.

Contrairement à la nature dynamique des applications pilotées par les événements, LangGraph utilise un schéma statique qui reste cohérent tout au long de l'exécution. Il simplifie la communication entre les nœuds, permettant aux développeurs de s'appuyer sur un format stable, garantissant ainsi une communication fluide au niveau des bords.

Conception d'un flux de travail de base

L'ingénierie des flux dans LangGraph peut être conceptualisée comme la conception d'une machine d'état. Dans ce paradigme, chaque nœud représente un état ou une étape de traitement distinct, tandis que les arêtes définissent les transitions entre ces états. Cette approche est particulièrement avantageuse pour les développeurs qui cherchent à trouver un équilibre entre les séquences de tâches déterministes et les capacités décisionnelles dynamiques de l'IA. Commençons à construire notre flux en initialisant StateGraph avec la classe TicketState que nous avons définie précédemment.

    from langgraph.graph import StateGraph, START, END
    
    workflow = StateGraph(TicketState)

Ajout de nœuds: Les nœuds sont des éléments fondamentaux, définis pour exécuter des tâches spécifiques telles que la classification de la priorité d'un ticket ou l'identification de son sujet.

Chaque fonction de nœud reçoit l'état actuel, effectue son opération et renvoie un dictionnaire permettant de mettre à jour l'état:

   def classify_priority_node(state: TicketState) -> TicketState:
        """Node to classify ticket priority."""
        priority = classify_priority.invoke({"email_body": state["email_body"]})
        return {"priority": priority}

    def identify_topic_node(state: TicketState) -> TicketState:
        """Node to identify ticket topic."""
        topic = identify_topic.invoke({"email_body": state["email_body"]})
        return {"topic": topic}
        
        
    workflow.add_node("classify_priority", classify_priority_node)
    workflow.add_node("identify_topic", identify_topic_node)

Les méthodes classify_priority_node et identify_topic_node modifieront le TicketState et enverront la saisie du paramètre.

Création des bords: Définissez les bords pour connecter les nœuds:


    workflow.add_edge(START, "classify_priority")
    workflow.add_edge("classify_priority", "identify_topic")
    workflow.add_edge("identify_topic", END)

The classify_priority establishes the start, whereas the identify_topic determines the end of our workflow so far.

Compilation et exécution: Une fois les nœuds et les bords configurés, compilez le flux de travail et exécutez-le.


    graph = workflow.compile()
    result = graph.invoke(initial_state)

Très bien! Vous pouvez également générer une représentation visuelle de notre flux LangGraph.

graph.get_graph().draw_mermaid_png(output_file_path="graph.png")

Si vous exécutez le code jusqu'à ce point, vous obtiendrez un graphe similaire au suivant:

first_graph.png

Cette illustration visualise une exécution séquentielle: démarrage, suivi du classement des priorités, puis identification du sujet et enfin terminaison.

L'un des aspects les plus puissants de LangGraph est sa flexibilité, qui nous permet de créer des flux et des applications plus complexes. Par exemple, nous pouvons modifier le flux de travail pour ajouter des bords depuis START vers les deux nœuds avec la ligne suivante:

    workflow.add_edge(START, "classify_priority")
    workflow.add_edge(START, "identify_topic")

Cette modification aura pour conséquence que l'agent exécutera simultanément classify_priority et identify_topic.

Une autre fonctionnalité très utile de LangGraph est la possibilité d'utiliser des bords conditionnels. Ils permettent au flux de travail de se ramifier en fonction de l'évaluation de l'état actuel, ce qui permet un routage dynamique des tâches.

Améliorons notre flux de travail. Nous allons créer un nouvel outil qui analyse le contenu, la priorité et le sujet de la demande afin de déterminer s'il s'agit d'un problème hautement prioritaire nécessitant une escalade (c'est-à-dire l'ouverture d'un ticket pour être traité par une équipe humaine). Si ce n'est pas le cas, une réponse automatisée sera générée pour l'utilisateur.


    @tool
    def make_escalation_decision(email_body: str, priority: str, topic: str) -> str:
        """Decide whether to auto-respond or escalate to IT team."""
        prompt = ChatPromptTemplate.from_template(
            """Based on this IT support ticket, decide whether to:
            - "auto_respond": Send an automated response for simple/common or medium priority issues
            - "escalate": Escalate to the IT team for complex/urgent issues
            
            Email: {email}
            Priority: {priority}
            Topic: {topic}
            
            Consider: High priority items usually require escalation, while complex technical issues necessitate human review.
            
            Respond with only: auto_respond or escalate"""
        )
        chain = prompt | llm
        response = chain.invoke({
            "email": email_body,
            "priority": priority,
            "topic": topic
        })
        return response.content.strip()
        

De plus, si la demande est jugée de priorité faible ou moyenne (ce qui entraîne une décision "auto_respond"), nous effectuerons une recherche vectorielle pour récupérer les réponses historiques. Ces informations seront ensuite utilisées pour générer une réponse automatisée appropriée. Cependant, cela nécessitera deux outils supplémentaires:


    @tool
    def retrieve_examples(email_body: str) -> str:
        """Retrieve relevant examples from past responses based on email_body."""
        try:
            examples = iris.cls(__name__).Retrieve(email_body)
            return examples if examples else "No relevant examples found."
        except:
            return "No relevant examples found."

    @tool
    def generate_reply(email_body: str, topic: str, examples: str) -> str:
        """Generate a suggested reply based on the email, topic, and RAG examples."""
        prompt = ChatPromptTemplate.from_template(
            """Generate a professional IT support response based on:
            
            Original Email: {email}
            Topic Category: {topic}
            Example Response: {examples}
            
            Create a helpful, professional response that addresses the user's concern.
            Keep it concise and actionable."""
        )
        chain = prompt | llm
        response = chain.invoke({
            "email": email_body,
            "topic": topic,
            "examples": examples
        })
        return response.content.strip()

Maintenant, définissons les nœuds correspondants à ces nouveaux outils:

    
    def decision_node(state: TicketState) -> TicketState:
        """Node to decide on escalation or auto-response."""
        decision = make_escalation_decision.invoke({
            "email_body": state["email_body"],
            "priority": state["priority"],
            "topic": state["topic"]
        })
        return {"decision": decision}
        
    
    def rag_node(state: TicketState) -> TicketState:
        """Node to retrieve relevant examples using RAG."""
        examples = retrieve_examples.invoke({"email_body": state["email_body"]})
        return {"rag_examples": examples}

    def generate_reply_node(state: TicketState) -> TicketState:
        """Node to generate suggested reply."""
        reply = generate_reply.invoke({
            "email_body": state["email_body"],
            "topic": state["topic"],
            "examples": state["rag_examples"]
        })
        return {"suggested_reply": reply}
        
    
    def execute_action_node(state: TicketState) -> TicketState:
        """Node to execute final action based on decision."""
        if state["decision"] == "escalate":
            action = f"&#x1f6a8; ESCALATED TO IT TEAM\nPriority: {state['priority']}\nTopic: {state['topic']}\nTicket created in system."
            print(f"[SYSTEM] Escalating ticket to IT team - Priority: {state['priority']}, Topic: {state['topic']}")
        else:
            action = f"&#x2705; AUTO-RESPONSE SENT\nReply: {state['suggested_reply']}\nTicket logged for tracking."
            print(f"[SYSTEM] Auto-response sent to user - Topic: {state['topic']}")
        
        return {"final_action": action}
        
        
        
    workflow.add_node("make_decision", decision_node)
    workflow.add_node("rag", rag_node)
    workflow.add_node("generate_reply", generate_reply_node)
    workflow.add_node("execute_action", execute_action_node)

Le bord conditionnel utilisera alors le résultat du nœud make_decision pour diriger le flux:

    workflow.add_conditional_edges(
        "make_decision",
        lambda x: x.get("decision"),
        {
            "auto_respond": "rag",
            "escalate": "execute_action"
        }
    )

Si l'outil make_escalation_decision (via decision_node) renvoie "auto_respond", le workflow passe par le nœud rag (pour récupérer des exemples), puis par generate_reply (pour rédiger la réponse) et enfin par execute_action (pour enregistrer la réponse automatique).

En revanche, si la décision est “escalate”, le flux contournera le RAG et passera aux étapes de génération, passant directement à execute_action pour gérer l'escalade. Pour compléter le graphe en ajoutant les bords standard restants, procédez comme suit:

    workflow.add_edge("rag", "generate_reply")
    workflow.add_edge("generate_reply", "execute_action")
    workflow.add_edge("execute_action", END)

Remarque sur le jeu de données: pour ce projet, le jeu de données utilisé pour alimenter la génération augmentée par récupération (RAG) provient du jeu de données Customer Support Tickets dataset on Hugging Face. Le jeu de données a été filtré afin de n'inclure que les éléments classés dans la catégorie de support technique 'Technical Support' et limité aux saisies en anglais English. Cela a permis de garantir que le système RAG ne récupère que des exemples très pertinents et spécifiques au domaine pour les tâches de support technique.

À ce stade, notre graphique devrait ressembler au suivant:

graph.png

Lorsque vous exécutez ce graphe avec un e-mail qui entraîne une classification de priorité élevée et une décision "escalate", vous obtenez la réponse suivante:

image.png

Au même moment, une demande classée comme faible priorité et donnant lieu à une décision « auto_respond » déclenchera une réponse similaire à la suivante:

image.png

Alors... Est-ce que tout est rose?

Pas tout à fait. Il y a quelques obstacles à éviter:

  • Confidentialité des données: Soyez prudent avec les informations sensibles, ces agents nécessitent des mesures de protection.
  • Coûts informatiques: Certaines configurations avancées nécessitent des ressources importantes.
  • Hallucinations: Les LLM peuvent parfois inventer des choses (même s'ils restent plus intelligents que la plupart des stagiaires).
  • Non-déterminisme: Une même saisie peut donner des résultats différents, ce qui est excellent pour la créativité, mais problématique pour les processus rigoureux.

Cependant, la plupart de ces lacunes peuvent être comblées grâce à une bonne planification, aux bons outils et, bien sûr, à un peu de réflexion.

LangGraph transforme les agents IA, qui ne sont encore que des mots à la mode, en solutions fonctionnelles et tangibles. Que vous souhaitiez automatiser votre service client, traiter vos tickets informatiques ou créer des applications autonomes, ce framework rend tout cela possible et même agréable.

Avez-vous des questions ou des commentaires? Parlons-en. La révolution de l'IA a besoin de créateurs comme vous.

0
0 27
Article Developer Community Admin · Juil 25, 2025 10m read

Confrontés aux volumes considérables et sans cesse croissants de données générées dans le monde aujourd'hui, les architectes logiciels doivent accorder une attention particulière à l'évolutivité de leurs solutions. Ils doivent également concevoir des systèmes capables, si nécessaire, de gérer plusieurs milliers d'utilisateurs simultanés. Ce n'est pas facile, mais il est absolument indispensable de concevoir des systèmes hautement évolutifs.

On compare une charge de travail moyenne de 1 000 requêtes de 1 kilo-octet par seconde à une autre impliquant 10 requêtes de 1 téraoctet par heure

Les architectes logiciels disposent de plusieurs options pour concevoir des systèmes évolutifs. Ils peuvent procéder à une évolutivité verticale en utilisant des machines plus puissantes dotées de dizaines de processeurs. Ils peuvent utiliser des techniques de distribution (réplication) des données pour procéder à une évolutivité horizontale afin d'accueillir un nombre croissant d'utilisateurs. Et ils peuvent faire évoluer le volume de données horizontalement grâce à une stratégie de partitionnement des données. Dans la pratique, les architectes logiciels emploient plusieurs de ces techniques, en trouvant un compromis entre les coûts liés au matériel, la complexité du code et la facilité de déploiement afin de répondre à leurs besoins spécifiques.

Cet article explique comment la plateforme de données InterSystems IRIS Data Platform prend en charge l'évolutivité verticale et horizontale des volumes de données et d'utilisateurs. Il présente plusieurs options de distribution et de partitionnement des données et/ou du volume d'utilisateurs, en donnant des exemples de scénarios où chaque option serait particulièrement utile. Enfin, cet article explique comment InterSystems IRIS contribue à simplifier la configuration et le provisionnement des systèmes distribués.

Évolutivité verticale

La manière la plus simple d'évoluer est peut-être de le faire “verticalement” – c'est-à-dire en déployant le système sur une machine plus puissante, dotée de plus de processeurs et de mémoire. InterSystems IRIS prend en charge le traitement parallèle de SQL et inclut une technologie permettant d'optimiser l'utilisation des processeurs dans les machines multi-cœurs.

Cependant, l'évolutivité verticale présente des limites pratiques. D'une part, même la plus grande machine disponible peut ne pas être capable de gérer les volumes de données et les charges de travail considérables requis par les applications modernes. D'autre part, les "supercalculateur" peuvent être extrêmement coûteux. De nombreuses organisations trouvent plus rentable d'acheter, par exemple, quatre serveurs à 16 cœurs plutôt qu'une machine à 64 cœurs.

La prévision de la capacité pour les architectures à serveur unique peut s'avérer difficile, en particulier La capacité de gérer les pics de charge peut entraîner une sous-utilisation coûteuse pendant les heures creuses. D'un autre côté, un nombre insuffisant de cœurs peut ralentir considérablement les performances pendant les périodes de forte utilisation. De plus, augmenter la capacité d'une architecture à serveur unique implique l'achat d'une nouvelle machine. Il est impossible de rajouter de la capacité en cours de fonctionnement.

En résumé, bien qu'il soit important que les logiciels exploitent tout le potentiel du matériel sur lequel ils sont déployés, l'évolutivité verticale seule ne suffit pas pour répondre à toutes les charges de travail, sauf les plus statiques.

Évolutivité horizontale

Pour toutes les raisons évoquées ci-dessus, la plupart des entreprises qui recherchent une évolutivité massive déploient leurs systèmes en réseau, en faisant évoluer les charges de travail et/ou les volumes de données "horizontalement", c'est-à-dire en répartissant le travail entre plusieurs serveurs. En général, chaque serveur du réseau est une machine abordable, mais des serveurs plus puissants peuvent être utilisés si nécessaire pour tirer parti également de l'évolutivité verticale.

Les architectes logiciels savent bien qu'il n'existe pas deux charges de travail identiques. Certaines applications modernes peuvent être utilisées simultanément par des centaines de milliers d'utilisateurs, ce qui génère un nombre très élevé de petites transactions par seconde. D'autres peuvent n'avoir qu'une poignée d'utilisateurs, mais interroger des pétaoctets de données. Ces deux types de charge de travail sont très exigeants, mais ils nécessitent des approches différentes en matière d' évolutivité. Nous allons d'abord examiner chaque scénario séparément.

Évolutivité horizontale du volume d'utilisateurs

Pour prendre en charge un nombre considérable d'utilisateurs (ou de transactions) simultanés, InterSystems IRIS s'appuie sur une technologie unique de mise en cache des données appelée Enterprise Cache Protocol (ECP).

Dans un réseau de serveurs, l'un d'entre eux sera configuré comme serveur de donnéesdans lequel les données seront conservées. Les autres seront configurés comme serveurs d'applications. Chaque serveur d'applications exécute une instance d'InterSystems IRIS et présente les données à l'application comme s'il s'agissait d'une base de données locale. Les données ne sont pas conservées sur les serveurs d'applications. Ceux-ci sont là pour fournir de la mémoire cache et de la puissance de traitement du processeur.

Les sessions utilisateur sont réparties entre les serveurs d'application, généralement via un équilibreur de charge, et les requêtes sont traitées à partir du cache du serveur d'application local, si possible. Les serveurs d'application ne récupèrent les données du serveur de données que si nécessaire. InterSystems IRIS synchronise automatiquement les données entre tous les participants du cluster.

Le travail de calcul étant pris en charge par les serveurs d'application, le serveur de données peut être principalement dédié au stockage permanent des résultats des transactions. Les serveurs d'application peuvent être facilement ajoutés au cluster ou supprimés de celui-ci en fonction de la charge de travail. Par exemple, dans le cas d'une utilisation dans le secteur de la vente au détail, vous pouvez ajouter quelques serveurs d'application pour faire face à la charge exceptionnelle du Black Friday, puis les désactiver une fois la période de soldes terminée.

Les serveurs d'applications sont particulièrement utiles pour les applications où un grand nombre de transactions doivent être effectuées, mais où chaque transaction n'affecte qu'une partie relativement restreinte de l'ensemble des données. Les déploiements qui utilisent des serveurs d'applications avec ECP ont démontré leur capacité à prendre en charge plusieurs milliers d'utilisateurs simultanés dans divers secteurs.

Évolutivité horizontale du volume de données

Lorsque des requêtes (généralement analytiques) doivent accéder à une grande quantité de données, le "jeu de données de travail" qui doit être mis en cache afin de prendre en charge efficacement la charge de travail des requêtes peut dépasser la capacité mémoire d'une seule machine. InterSystems IRIS fournit une fonctionnalité appelée "partitionnement" qui partitionne physiquement les grandes tables de base de données sur plusieurs instances de serveur. Les applications accèdent toujours à une seule table logique sur une instance désignée comme master de partition. Le master de partition décompose les requêtes entrantes et les envoie aux serveurs de partition, chacun contenant une partie distincte des données de la table et des index associés. Les serveurs de partition traitent les requêtes locales à la partition de manière parallèle, puis renvoient leurs résultats au serveur de partition pour agrégation.

Les données sont partitionnées entre les serveurs de partition selon une clé de partition, qui peut être gérée automatiquement par le système ou définie par l'architecte logiciel en fonction des colonnes sélectionnées de la table. Grâce à une sélection rigoureuse des clés de partition, les tables qui sont souvent jointes peuvent être co-partitionnées, de sorte que les lignes de ces tables qui seraient normalement jointes sont stockées sur le même serveur de partition, ce qui permet à la jointure de se faire entièrement au niveau local sur chaque serveur de partition, maximisant ainsi la parallélisation et les performances.

À mesure que le volume de données augmente, des partitions supplémentaires peuvent être facilement ajoutées. Le partitionnement est totalement transparent pour l'application et les utilisateurs.

Toutes les tables ne doivent pas nécessairement être partitionnées. Par exemple, dans les applications analytiques, les tables de faits (par exemple, les commandes dans un scénario de vente au détail) sont généralement très volumineuses et seront partitionnées. La partitionnement ne concerne pas les tables beaucoup plus petites (par exemple, les produits, les points de vente, etc.). Les tables non partitionnées sont conservées sur le master de partition. Si une requête nécessite des jointures entre des tables partitionnées et non partitionnées tables, ou si des données provenant de deux partitions différentes doivent être jointes, InterSystems IRIS utilise un mécanisme ECP très efficace pour répondre correctement et efficacement à la requête. Dans ces cas, InterSystems IRIS ne partage entre les partitions que les lignes nécessaires, plutôt que de diffuser des tables entières sur le réseau, comme le feraient de nombreuses autres technologies. InterSystems IRIS améliore de manière transparente l'efficacité et les performances des charges de travail de requêtes relatives au Big Data grâce au partitionnement, sans limiter les types de requêtes qui peuvent être satisfaites.

L'architecture InterSystems IRIS permet des jointures multi-tables complexes lors de l'interrogation d'ensembles de données distribués et partitionnés sans nécessiter de co-partitionnement, sans réplication des données et sans diffusion de tables entières sur les réseaux.

Évolutivité du volume des utilisateurs et des données

De nombreuses solutions modernes doivent prendre en charge simultanément un taux de transaction élevé (volume d'utilisateurs) et l'analyse de grands volumes de données. Prenons l' exemple: une application de gestion de patrimoine privé qui fournit des tableaux de bord résumant les portefeuilles et les risques des clients, en temps réel, à partir des données actuelles du marché.

InterSystems IRIS permet de telles applications de traitement hybride transactionnel et analytique (HTAP) en autorisant l'utilisation combinée de serveurs d'applications et de partitionnement. Des serveurs d'applications peuvent être ajoutés à l'architecture illustrée à la figure #2 afin de répartir la charge de travail sur le master de partition. Les charges de travail et les volumes de données peuvent être dimensionnés indépendamment les uns des autres, en fonction des besoins de l'application.

Lorsque les applications exigent une évolutivité maximale (par exemple, si un modèle prédictif doit évaluer chaque enregistrement d'une table volumineuse alors que de nouveaux enregistrements sont ingérés et interrogés simultanément), chaque partition de données individuelle peut agir comme serveur de données dans un modèle ECP. Les serveurs d'application qui partagent les charges de travail sur les partitions de données sont appelés "partitions de requêtes". Ceci, combiné aux mécanismes transparents garantissant la haute disponibilité d'un cluster InterSystems IRIS, fournit aux architectes de solutions tout ce qu'il faut pour satisfaire les exigences uniques de leur solution en matière d'évolutivité et de fiabilité.

Les performances et l'efficacité comparatives de l'approche de partitionnement d'InterSystems IRIS ont été démontrées et documentées dans un test de référence validé par une grande société d'analyse technologique. Lors de tests réalisés à partir d'un cas d'utilisation réel dans le domaine des services financiers critiques, InterSystems IRIS s'est révélé plus rapide que plusieurs bases de données hautement spécialisées, tout en nécessitant moins de matériel et en accédant à davantage de données.

Déploiement flexible

InterSystems IRIS offre aux développeurs de logiciels une grande flexibilité pour la conception de solutions hautement efficaces et évolutives. Mais l'évolutivité peut s'accompagner d'une complexité accrue, notamment en raison de l'ajout de serveurs supplémentaires à l'architecture, qui assument alors diverses fonctions. Simplifiez le provisionnement et le déploiement des serveurs (physiques ou virtuels) dans une architecture distribuée avec InterSystems IRIS.

InterSystems permet d'utiliser des scripts simples pour configurer les conteneurs InterSystems IRIS en tant que serveur de données, serveur de partition, master de partition, serveur d'application, etc. Les conteneurs peuvent être facilement déployés dans des clouds publics ou privés. Ils sont également faciles à mettre hors service, ce qui permet de concevoir des architectures évolutives pouvant s'adapter à des besoins fluctuants.

Conclusion

Une évolutivité massive est indispensable pour les applications modernes, en particulier les applications hybrides de traitement transactionnel et analytique qui doivent gérer simultanément des charges de travail et des volumes de données très importants. La plateforme de données InterSystems IRIS offre aux architectes logiciels des options pour faire évoluer leurs applications de manière économique. Elle prend en charge l'évolutivité verticale, les serveurs d'applications pour l'évolutivité horizontale du volume d'utilisateurs et une approche très efficace du partitionnement pour l'évolutivité horizontale du volume de données, qui élimine le besoin de diffusions sur le réseau. Toutes ces technologies peuvent être utilisées indépendamment ou en combinaison pour adapter une architecture évolutive aux exigences spécifiques d'une application.


Plus d'articles sur le sujet:

Source: Évolutivité massive avec la plateforme de données InterSystems IRIS

0
0 15
InterSystems officiel Adeline Icard · Juil 25, 2025

Les versions de maintenance 2025.1.1 de la plateforme de données InterSystems IRIS, d'InterSystems IRIS for Health et de HealthShare Health Connect sont désormais disponibles en disponibilité générale (GA). N'hésitez pas à partager vos commentaires via la Communauté des développeurs afin que nous puissions développer ensemble un produit plus performant.

Documentation

Vous trouverez les listes détaillées des modifications et les listes de contrôle des mises à niveau sur les pages suivantes :

0
0 23
InterSystems officiel Adeline Icard · Juil 24, 2025

InterSystems annonce la disponibilité générale d'InterSystems IRIS 2025.2

InterSystems a le plaisir d'annoncer la disponibilité générale (GA) de la version 2025.2 de la plateforme de données InterSystems IRIS. Il s'agit d'une version en livraison continue (CD). Veuillez noter que les versions GA d'InterSystems IRIS for Health et HealthShare Health Connect 2025.2 sont actuellement suspendues en raison de limitations de mise en miroir introduites par les mises à jour de sécurité (détails ci-dessous).

Points forts de la version

0
0 27
InterSystems officiel Adeline Icard · Juil 23, 2025
InterSystems Reports version 25.1 est désormais disponible sur le site de distribution de logiciels InterSystems, dans la section Composants. Le logiciel, baptisé InterSystems Reports Designer et InterSystems Reports Server, est disponible pour les systèmes d'exploitation Mac OSX, Windows et Linux.
Cette nouvelle version apporte des améliorations et des correctifs de notre partenaire Insightsoftware. InterSystems Reports 25.1 est optimisé par Logi Report version 25.1, qui inclut :

la prise en charge de la construction dynamique des objets lors de la distribution planifiée des rapports par

0
0 20
InterSystems officiel Adeline Icard · Juil 22, 2025

InterSystems a le plaisir d'annoncer la sortie de la version 3.0.5 de l'extension VS Code - ObjectScript. Cette version inclut de nombreuses corrections de bugs, ainsi que des modifications des données de télémétrie collectées. La collecte de données d'utilisation supplémentaires permet à InterSystems d'identifier et de prioriser les correctifs et améliorations les plus bénéfiques pour vous, nos utilisateurs. Les informations personnelles identifiables (PII) ne seront jamais collectées et la télémétrie peut être désactivée via le paramètre telemetry.telemetryLevel. La liste complète des

0
0 24
Article Sylvain Guilbaud · Juil 18, 2025 11m read

🛠️ Gestion des configurations d'InterSystems API Manager (IAM = Kong Gateway) en CI/CD

🔍 Contexte : configurations d'InterSystems IAM

Dans le cadre de l'intégration d'InterSystems IAM dans un environnement sécurisé et contrôlé, InterSystems IAM repose sur Kong Gateway pour gérer les API exposées. Kong agit comme une API Gateway moderne, capable de gérer l’authentification, la sécurité, la gestion du trafic, les plugins, et bien plus encore.

0
0 24
Article Sylvain Guilbaud · Juil 8, 2025 3m read

Si vous migrez d'Oracle vers InterSystems IRIS, comme beaucoup de mes clients, vous risquez de rencontrer des modèles SQL spécifiques à Oracle nécessitant une conversion.

Prenons l'exemple suivant:

SELECT (TO_DATE('2023-05-12','YYYY-MM-DD') - LEVEL + 1) AS gap_date
FROM dual
CONNECT BY LEVEL <= (TO_DATE('2023-05-12','YYYY-MM-DD') - TO_DATE('2023-05-02','YYYY-MM-DD') + 1);

Dans Oracle:

  • LEVEL est une pseudo-colonne utilisée dans les requêtes hiérarchiques (CONNECT BY). Elle commence à 1 et s'incrémente de 1.
  • CONNECT BY LEVEL <= (...) détermine le nombre de lignes à générer.
  • La différence entre les deux dates plus un donne 11, donc la requête génère 11 lignes, en comptant à rebours à partir du 12 mai 2023 jusqu'au 2 mai 2023.

Répartition du résultat:

LEVEL = 1  → 2023-05-12
LEVEL = 2  → 2023-05-11
...
LEVEL = 11 → 2023-05-02

La question est maintenant de savoir comment obtenir ce résultat dans InterSystems IRIS, qui ne prend pas en charge CONNECT BY?

Une solution consiste à implémenter une requête de type SQL à l'aide d'ObjectScript qui imite ce comportement. Vous trouverez ci-dessous un exemple de définition CREATE QUERY qui accepte une date de début STARTDATE et un nombre de jours DAYS, et renvoie la liste des dates par ordre descendant.


✅ InterSystems IRIS: mise en œuvre d'une requête de l'intervalle de date

CREATE QUERY GET_GAP_DATE(IN STARTDATE DATE, IN DAYS INT)
  RESULTS (GAP_DATE DATE)
  PROCEDURE
  LANGUAGE OBJECTSCRIPT

Execute(INOUT QHandle BINARY(255), IN STARTDATE DATE, IN DAYS INT) { SET QHandle("start") = STARTDATE SET QHandle("days") = DAYS SET QHandle("level") = 1 RETURN $$$OK }

Fetch(INOUT QHandle BINARY(255), INOUT Row %List, INOUT AtEnd INT) { IF (QHandle("level") > QHandle("days")) { SET Row = "" SET AtEnd = 1 } ELSE { SET Row = $ListBuild(QHandle("start") - QHandle("level") + 1) SET QHandle("level") = QHandle("level") + 1 } RETURN $$$OK }

Close(INOUT QHandle BINARY(255)) { KILL QHandle QUIT $$$OK }

Vous pouvez exécuter la commande CREATE QUERY Vous pouvez exécuter la commande CREATE QUERY ci-dessus dans le portail IRIS System Management, ou via un outil tel que DBeaver ou un éditeur Python/Jupyter Notebook utilisant JDBC/ODBC.


🧪 Exemple d'utilisation:

Pour générer le même résultat que la requête Oracle ci-dessus, utilisez:

SELECT * FROM GET_GAP_DATE(
  TO_DATE('2023-05-12', 'YYYY-MM-DD'),
  TO_DATE('2023-05-12', 'YYYY-MM-DD') - TO_DATE('2023-05-02', 'YYYY-MM-DD') + 1
);

Cela donnera le résultat suivant:

GAP_DATE
----------
2023-05-12
2023-05-11
...
2023-05-02
(11 rows)

🔁 Utilisation avancée: Jointure avec d'autres tables

Vous pouvez également utiliser cette requête comme sous-requête ou dans des jointures:

SELECT * 
FROM GET_GAP_DATE(TO_DATE('2023-05-12', 'YYYY-MM-DD'), 11) 
CROSS JOIN dual;

Cela vous permet d'intégrer des plages de dates dans des flux SQL plus importants.


J'espère que cela sera utile à tous ceux qui sont confrontés à des scénarios de migration d'Oracle vers IRIS ! Si vous avez mis au point des solutions alternatives ou si vous proposez des améliorations, n'hésitez pas à me faire part de vos commentaires.

0
0 31
Question Corentin Blondeau · Juin 4, 2025

Bonjour,
Quels sont les avantages et les inconvénients d'avoir plusieurs namespaces par rapport à un seul?
J'aimerais bien savoir les aspects positifs et négatifs des deux cas de figures, par exemple 1 ou 4 espaces de nom pour un total de 20 flux.

Qu'en est t'il niveau sécurité, gestion serveur, gestion du code, autre?
En terme de performances, est-ce que trop d'items sur une seul production ralenti le serveur? Ou trop de namespaces augmente la consommation mémoire pour rien?
Comment évolue la consommation d'un namespace? Il y a un minimum?
Je vous remercie de vos réponses.
Corentin BLONDEAU

5
1 92
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 Guillaume Rongier · Juil 2, 2025 4m read

Bonjour chers ingénieurs interface et développeurs d'applications,     

Saviez-vous que Python peut être utilisé dans des productions ou des intégrations?

L'interface utilisateur de configuration de production est low-code, mais dans de nombreux projets, vous pouvez arriver au point où il est nécessaire d'écrire du code. Comme nous l'avons vu dans le cours Architecture d'intégration, les Business Process comportent de nombreux emplacements où insérer du code, notamment l'éditeur BPL (pour les Business Process BPL) et l'éditeur DTL (pour les transformations de données).

😯 À partir d'InterSystems IRIS 2025.1, Python et InterSystems ObjectScript sont tous deux pris en charge dans les éditeurs BPL et DTL. 😄 Vous pouvez choisir entre ces langues selon vos préférences personnelles, et pas selon des besoins techniques. 😍

Rédaction du code Python dans l'éditeur BPL

Python est pris en charge dans l'éditeur BPL depuis InterSystems IRIS 2024.1. Examinons un Business Process BPL:

Business Process BPL comprend les activités suivantes (dans l'ordre) : activité Début, activité Affectation, activité Code avec un logo Python, activité If avec un logo Python, activité Transformation du côté vrai de l'activité If, et activité Fin.

Dans l'activité <code>, GetWinningBid, remarquez l'icône Python. En ouvrant cette activité <code>, on peut voir que le champ intitulé Code contient des instructions Python! Ces instructions utilisent la bibliothèque intégrée iris pour ouvrir un objet d'un ID donné.

Une activité Code provenant d'un éditeur BPL. Le langage est défini sur Python. Le code indique # Utilisation du corps de la requête pour ouvrir l'objet Bid avec l'ID donné. New line. bidID = request.id. New line. context.winningBid = iris.cls("AuctionServer.Bid")._OpenId(bidID).

Vous pouvez définir le langage pour l'ensemble du BPL (Python ou ObjectScript) via le paramètre Language dans l'onglet General u BPL, puis utiliser le menu déroulant Language Override (Remplacement du language) dans un champ de code donné pour remplacer la valeur par défaut du BPL.

L'activité <code> n'est pas la seule activité qui analyse le code dans un Business Process BPL. L'activité <code> analyse les instructions de code complètes, mais autres champs des activités BPL acceptent également des expressions de code. Vous pouvez les reconnaître grâce au menu déroulant Language Override (Remplacement du langage), où vous pouvez remplacer la valeur par défaut de BPL. 

Par exemple, l'activité <if>, qui crée un flux logique conditionnel, a un champ appelé Condition qui accepte des expressions de code. Il a un champ correspondant appelé Condition Language Override (Remplacement du langage de condition). Dans cet exemple, ce champ est défini sur Python.

Lorsque Python n'était pas encore pris en charge dans ces domaines, les ingénieurs d'interface devaient savoir comment écrire des expressions logiques dans ObjectScript. Par exemple, vous deviez savoir que || signifie Or, et && signifie And. Vous pouvez désormais utiliser Python, dont la syntaxe pour les expressions logiques est plus proche du langage naturel.

Dans l'éditeur BPL, s'il vous faut importer des paquetages Python, vous pouvez le faire sous les instructions Python From/Import dans l'onglet General.

Rédaction du code Python dans l'éditeur DTL 

Python est également pris en charge dans les champs de code de l'éditeur DTL depuis InterSystems IRIS 2025.1. Les utilisations courantes du code personnalisé dans DTL sont le formatage de chaînes et les calculs.

Une transformation de données DTL convertissant un objet source, AuctionServer.Bid, into a target object, AuctionServer.Order

Si vous devez importer des paquetages Python dans l'éditeur DTL, recherchez l'onglet Paramètres Transformation et utilisez le champ d' instruction Python From / Import. Le langage par défaut pour les expressions de code peut également être défini dans l'onglet d'instructions Transformation .

Dans l'onglet Paramètres de transformation du DTL, le langage est défini sur Python et le champ des instructions Python From / Import contient la mention from numpy import random.

Pour trouver les champs d'analyse de code des actions DTL, il suffit de rechercher tout champ associé à une liste déroulante Language Override. Par exemple, dans l'action <code> du DTL, vous pouvez écrire des instructions de code complètes. Les actions telles que Set, If, Trace et autres prennent en charge les expressions de code, qui peuvent être écrites en Python ou en ObjectScript.

Une activité de code issue d'une transformation de données DTL, avec le langage défini sur Python. Le contenu du champ de code indique bidders = [bid.User for bid in source.Lot.Bids()]. New line. bidders = set(bidders). New line. target.NumOutbid = (len(bidders) - 1).

Flux de travail axés sur le code

Si vous maîtrisez le processus d'édition de productions via un code, vous pouvez modifier les fichiers BPL et DTL via leurs fichiers de configuration. Cette méthode n'est pas aussi simple que les interfaces de l'éditeur, mais elle est très intuitive pour les programmeurs expérimentés.

Vous pouvez également utiliser Python pour écrire des méthodes dans des fonctions utilitaires personnalisées et des composants personnalisés, tels que des opérations commerciales et des services commerciaux (à l'exception des méthodes de rappel callback methods, comme défini dans la documentation).

Récapitulatif 

  • L'éditeur DTL et l'éditeur BPL prennent désormais en charge le code et les expressions Python. 
  • Si nécessaire, indiquez les paquetages que vous souhaitez importer via les paramètres généraux de BPL ou DTL. 
  • Vous pouvez définir un langage par défaut pour chaque DTL/BPL et remplacer des champs spécifiques si nécessaire.

Rejoignez la conversation!

  • Avez-vous déjà utilisé Python pour créer des intégrations personnalisées? 
  • Quelles questions avez-vous au sujet de la prise en charge de Python dans les intégrations?
  • Ajoutez vos commentaires ci-dessous!
0
0 28