New post

查找

Article
· Feb 4 6m read

REST Service in IRIS Production: When You Crave More Control Over Raw Data

 

  “At the request of the survivors, the names have been changed. Out of respect for the dead, the REST has been told exactly as it occurred.”

Warning: There’s an important poll at the end of this article. I’m eagerly awaiting your responses!
 

So, we hit a snag. A big one. There’s no way to search messages in our production.

Sure, a message comes into the REST service, and everything in the production runs smoothly as it should.



 

Here’s a sample request:

{

"case":"2-12-85-06",

"to":"management of ZOO",

"subject":"Children report that the horse has grown a fifth leg"
}

Everything looks great in production!

BUT! Then either we or the client wants more:

"Hey, can we filter messages by specific criteria? For example, did we ever get anything related to case number 2-12-85-06?"

And that's where things took a sinister turn.

The standard message type, EnsLib.HTTP.GenericMessage, stores JSON data in streams. A great idea, right? Until it’s not.


Because searching through messages in streams works about as well as a flyscreen door on a submarine. Spoiler: It doesn’t. Like, at all.

You’ve probably seen this error before:


This adorable little red “NOPE” is the vigilant gatekeeper of your data. Always on duty, always blocking your sweaty fingers from touching anything sensitive. A true protector! But also a huge headache.

 

The Fight Back

  "The red tide, Lester. Life. We’re being fed crap, day after day - bosses, wives, whatever. It’s killing us. And if you don’t stand up, if you don’t show them you’re still an animal deep down, the tide will sweep you away."

 

We don’t retreat, though. Never.

There are a few possible solutions. I personally went for a storage-limited one because I don’t want to keep extra stored data. I just want to stay within the realm of production messages.

So, here’s the plan:

We create a custom message class, inheriting from EnsLib.HTTP.GenericMessage. Something like this:

Class Production.Message.ZooMessage Extends EnsLib.HTTP.GenericMessage
{

Property RequestJSON As %String(MAXLEN = "");
Property CaseId As %String;
Index ZooMessageCaseIdIndex On CaseId;
}

Next, we need to pass this custom message to the operation and include additional search-ready data (message body and case number).

 

Solution 1: The “Not-So-Great Start”

 

 

“Don’t trust what comes out of the ocean.

- But... we came from the ocean.”

 

We create a service by inheriting from EnsLib.REST.GenericService, then add it to the production. Simple, right?

Class Production.Service.ZooREST Extends EnsLib.REST.GenericService
{

Parameter DOCCLASS = "Production.Message.ZooMessage";
}

Production:

  <Item Name="Service.RESTver1" Category="" ClassName="Production.Service.ZooREST">
    <Setting Target="Host" Name="TargetConfigName">Operation.RESTver1</Setting>
    <Setting Target="Adapter" Name="Port">1984</Setting>
  </Item>

Except it’s not.


The input message type stays the same.

Parameter DOCCLASS = "Production.Message.ZooMessage" didn’t work.

 


Solution 2: “Too Many Cooks”

 

  “This is quite unusual…
- Unusual? Once I found a human foot in the oven. That’s unusual. This is merely weird.”

 

Turns out, the issue boils down to this single line of code in the OnProcessInput method:


Replacing it with:
Set tRequest=$classmethod(..#DOCCLASS,"%New",pRequestBody,,pRequestBody)


...would fix the issue. But this change would render the vendor’s code in subsequent IRIS updates useless. A dead-end option that leaves a bad taste in the mouth.

 

 

Solution 3: The “Dragon-Guarded Path”

 

 

“You could just get in your car and drive away.

- And why’s that?

- Because some roads you just shouldn’t take. Used to be maps would mark them, saying, ‘Here Be Dragons.’ Now they don’t, but the dragons are still there.”

 

The idea? Pass the message to a router, transform it, and send it to the process.

Components needed:

  • A router
  • A transformation
  • A pinch of something mysterious and arcane
Class Production.Rule.Router Extends Ens.Rule.Definition
{

XData RuleDefinition [ XMLNamespace = "http://www.intersystems.com/rule" ]
{
<ruleDefinition alias="" context="EnsLib.MsgRouter.RoutingEngine" production="Production.Test">
<ruleSet name="ZOO Router" effectiveBegin="" effectiveEnd="">
<rule name="ZOO Process Inbound Http" disabled="false">
<constraint name="source" value="Service.RESTver3"></constraint>
<when condition="1">
<send transform="Production.DataTransformation.ZOO" target="Operation.RESTver3"></send>
<return></return>
</when>
</rule>
</ruleSet>
</ruleDefinition>
}

}

 

Class Production.DataTransformation.ZOO Extends Ens.DataTransformDTL [ DependsOn = (EnsLib.HTTP.GenericMessage, Production.Message.ZooMessage) ]
{

Parameter IGNOREMISSINGSOURCE = 1;
Parameter REPORTERRORS = 1;
Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;
XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='EnsLib.HTTP.GenericMessage' targetClass='Production.Message.ZooMessage' create='new' language='objectscript' >
<assign value='source.HTTPHeaders' property='target.HTTPHeaders' action='set' />
<assign value='source.Stream.Read(1000000)' property='target.RequestJSON' action='set' />
<assign value='{}.%FromJSON(source.Stream).case' property='target.CaseId' action='set' />
</transform>
}

}

A bit bulkier, yes, but it works. No ritual dances required.

 

The Verdict?

  “- Nobody killed a judge. We own all the judges. What'd be the point of killing one?”

 

Honestly, I can’t decide. That’s why I’m writing this.

So, here’s a question for you:

  1. Should we grovel before Intersystems and ask them to fix the code?
  2. Fix it ourselves with rusty tools and questionable ethics?
  3. Accept the current status quo and build something clunky but functional?
  4. ?

Your call.

 

 

PS: Code from the examples above is available in this GitHub repository: https://github.com/banksiaglobal/http-rest-production

 

Aleksandr Kolesov is a seasoned software developer from Banksia Global with over 30 years of experience in the tech industry, specializing in the InterSystems IRIS platform. With a deep understanding of high-performance data management and data intensive application architectures, he has played a pivotal role in developing and optimizing applications for enterprises across various sectors. Over the years, Alex has earned a reputation for his expertise in building scalable, efficient systems, as well as his passion for mentoring up-and-coming developers. When he's not coding or solving complex technical challenges, Alex enjoys exploring new technologies and contributing to the growing community of InterSystems developers.
 
   
Banksia Global is a leading technology consultancy that specializes in helping organizations harness the full potential of modern data management solutions. With a strong focus on the InterSystems IRIS platform, Banksia Global provides strategic guidance, system integration, and custom software development services to clients across various industries. Their expertise lies in delivering high-performance, scalable solutions that enable businesses to drive innovation, streamline operations, and unlock new opportunities. Known for its collaborative agile approach, Banksia Global works closely with clients to ensure that each solution is tailored to meet their specific needs, fostering long-term partnerships and sustainable growth.
 
10 Comments
Discussion (10)4
Log in or sign up to continue
Question
· Feb 4

How to use DTL remove property for a repeating segment in for each loop for target using copy

When removing a segment in DTL for hl7 using foreach the segment doesn't actually get removed and leaves a blank segment. 

i.e. 

MSH|^~\&|SendingSystem|ReceivingSystem|202301241000||ADT^A01|12345|P|2.4
EVN|S|202301241000
PID|1|MRN12345|1^^^^KEEP~2^^^^Remove~3^^^^Keep|M|19800101|  
PD1|PatientAddress^Street^City^State^Zip
PV1|I|INPATIENT|BED123|DoctorID|202301241000|202301241000

This blank segment to be removed

I read the notes and thought it might be about swizzling at https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls... but it wasn't an example i could understand. 

2 Comments
Discussion (2)3
Log in or sign up to continue
InterSystems Official
· Feb 4

Les versions de maintenance 2024.1.3 d'InterSystems IRIS, IRIS for Health et HealthShare HealthConnect sont désormais disponibles

Les dernières versions de maintenance étendue d'InterSystems IRIS, InterSystems IRIS for Health et HealthShare Health Connect sont désormais disponibles.

✅ 2024.1.3

La version 2024.1.3 fournit des correctifs de bogues pour toutes les versions 2024.1.x précédentes, y compris le correctif pour l'alerte suivante récemment émise - Alerte : Données non valides introduites dans la base de données et les fichiers journaux avec des....

Vous trouverez les listes de modifications détaillées et les listes de contrôle de mise à niveau sur ces pages :

Comment obtenir le logiciel

Le logiciel est disponible sous forme de packages d'installation classiques et d'images de conteneur. Pour obtenir la liste complète des programmes d'installation et des images de conteneur disponibles, veuillez consulter la page Web des plateformes prises en charge.

Les packages d'installation complets pour InterSystems IRIS et InterSystems IRIS for Health sont disponibles dans les kits complets de la plateforme de données InterSystems IRIS du WRC. Les kits HealthShare Health Connect sont disponibles sur la page HealthShare Full Kits du WRC.

Les images de conteneurs sont disponibles sur le registre de conteneurs InterSystems.

Le nombre total de kits et de conteneurs dans ces versions est :

  • 2024.1.3.456.0
Discussion (0)0
Log in or sign up to continue
Article
· Feb 4 3m read

Como ejecutar vuestra solución de InterSystems en un entorno de Kubernetes con garantía de calidad de servicio

Todos los pods reciben una asignación de Calidad de Servicio (QoS). Existen tres niveles de prioridad dentro de un nodo:

  1. Guaranteed: Alta prioridad
  2. Burstable: Prioridad media
  3. BestEffort: Baja prioridad

Es una forma de indicar al kubelet cuáles son vuestras prioridades en un nodo si es necesario recuperar recursos. Este fantástico GIF de Anvesh Muppeda lo explica.

Si es necesario liberar recursos, primero se expulsarán los pods con QoS BestEffort, luego los Burstable y, por último, los Guaranteed. La idea es que, al eliminar los pods de menor prioridad, se recuperen suficientes recursos en el nodo para evitar la expulsión de los pods Guaranteed.

Por ello, queremos que nuestras aplicaciones críticas se ejecuten con Guaranteed Quality of Service, y los pods de InterSystems definitivamente entran en esta categoría.

Consultad la aplicación de Open Exchange o el repositorio de GitHub adjunto para encontrar una plantilla que os ayude a actualizar vuestro IrisCluster y garantizar QoS Guaranteed para todos los pods de InterSystems.

Hasta ahora, es posible que hayáis estado desplegando mediante recursos específicos en el podTemplate:

podTemplate:
  spec:
    resources:
      requests:
        memory: "8Gi"
        cpu: "2"
      limits:
        memory: "8Gi"
        cpu: "2"

Pero, suponiendo que estáis utilizando lo siguiente en vuestro archivo values.yaml de IKO (este es el comportamiento predeterminado):

useIrisFsGroup: false 

Entonces, estaréis ignorando los initContainers y cualquier posible side-car, lo que hará que vuestro pod tenga únicamente Burstable QoS.

Según la documentación de Kubernetes sobre QoS:
Para que un pod reciba la clase de QoS Guaranteed, deben cumplirse las siguientes condiciones:

  • Cada contenedor dentro del pod debe tener un límite de memoria y una solicitud de memoria.
  • Para cada contenedor, el límite de memoria debe ser igual a la solicitud de memoria.
  • Cada contenedor dentro del pod debe tener un límite de CPU y una solicitud de CPU.
  • Para cada contenedor, el límite de CPU debe ser igual a la solicitud de CPU.

Esto incluye tanto los initContainers como los side-cars. Para especificar los recursos del initContainer, debéis sobrescribirlo:

      podTemplate:
        spec:
          initContainers:
          - command:
            - sh
            - -c
            - /bin/chown -R 51773:51773 /irissys/*
            image: busybox
            name: iriscluster-init
            resources:
              requests:
                memory: "8Gi"
                cpu: "2"
              limits:
                memory: "8Gi"
                cpu: "2"
            securityContext:
              runAsGroup: 0
              runAsNonRoot: false
              runAsUser: 0
            volumeMounts:
            - mountPath: /irissys/data/
              name: iris-data
            - mountPath: /irissys/wij/
              name: iris-wij
            - mountPath: /irissys/journal1/
              name: iris-journal1
            - mountPath: /irissys/journal2/
              name: iris-journal2
          resources:
            requests:
              memory: "8Gi"
              cpu: "2"
            limits:
              memory: "8Gi"
              cpu: "2"

Consultad un ejemplo completo de IrisCluster, incluyendo initContainers y side-cars, en la aplicación de Open Exchange adjunta.

Como alternativa, podéis considerar cambiar el comportamiento predeterminado de IKO en el archivo values.yaml a:

useIrisFsGroup: true 

para evitar initContainers en algunos escenarios, aunque pueden surgir complicaciones. Además, useIrisFsGroup realmente merece un artículo propio. Planeo escribir sobre ello próximamente.

Discussion (0)1
Log in or sign up to continue
Article
· Feb 4 4m read

整合性チェックの各方法の違いについて

これは InterSystems FAQ サイトの記事です。
 

整合性チェックを行う場合、管理ポータルやタスクマネージャーから行う方法と、ターミナルから ^Integrity ユーティリティを使用して行う方法があります。

こちらのトピックでは、それぞれの違いをご説明します。
 


管理ポータル、整合性チェックタスク、
および SYS.Database の IntegrityCheck メソッドから行う方法


こちらの方法は、選択した複数のデータベースや、1 つのデータベースに格納された選択可能な複数のグローバルを対象に整合性をチェックを行います。

管理ポータル
[システムオペレーション] > [データベース] 整合性チェック・ボタン
※チェックを行いたいネームスペース、必要に応じてグローバルを選択し、実行・ボタンをクリック

整合性チェックタスク
[システムオペレーション] > [タスクマネージャー] > [タスクスケジュール] 整合性チェック・タスク 


SYS.Database クラスの IntegrityCheck メソッド:

%SYS>do ##class(SYS.Database).IntegrityCheck($LB("c:\intersystems\iris\mgr\user\"))  // DB指定
%SYS>do ##class(SYS.Database).IntegrityCheck()      // 全データベース

 

これらの方法では、エラーが検出されたすべてのグローバルを自動的に再テストします。
誤検出対策のための再チェックを行うため、余分に時間がかかる場合があります。

2回目のパス以降でもエラーが検出された場合、エラーメッセージが出力されます。

詳細は以下のドキュメントをご覧ください。
整合性チェックの誤検出


メリット:誤検出対策のための自動再チェックを行える、グローバル単位でチェックを行える(※管理ポータル)
デメリット:エラー検出の際には再チェックを行うため、余分な時間がかかる場合がある
 


^Integrity ユーティリティを使用して行う方法


こちらの方法は、ターミナルより手動でチェックを行えます。複数データベースを指定することも可能です。

管理ポータル等で行う上記方法と異なり、誤検出対策のための再チェックは行いません。

つまり、1 回目のパスでエラーが検出されたグローバルの再チェックは行わないため、エラーが報告されたグローバルをご自身で再チェックして誤検出を排除することが重要になります。

実行手順は、ターミナルを開き、%SYS ネームスペースに切り替えて、do ^Integrity と入力します。

また、^Integrity エントリポイントを用いることで、プログラムによる使用も可能です。
こちらの方法は、使用する並列処理の最大数を指定することが可能なため、整合性チェックのパフォーマンスを調整することが可能です(プロセス数の既定は8)。
以下の ^Integrity エントリポイント(CheckList^Integrity)を使用して行います。
 

Do CheckList^Integrity(outputglobal,dirlist,stopafteranyerror,listofglolist,maxproc,partialcheck)

outputglobal:結果を格納するグローバル
dirlist:チェックするすべてのディレクトリの $LIST 
stopafteranyerror:エラー発生時の整合性チェックの動作
                            (1:エラー検出でディレクトリのチェック停止、0:既定値。エラー発生時にチェック続行)
listofglolist:グローバル名を指定した $LIST を複数含む $LIST
maxproc:使用する並列処理の最大数(既定値は8)
partialcheck:既定値 0 (この値は予期しない問題が発生した場合のフォールバックとしてのみ提供)

 

 

// 以下は、5 つのプロセスを使って 3 つのデータベースをチェックしている例
set dblist=$listbuild(“/data/db1/”,”/data/db2/”,”/data/db3/”)
set sc=$$CheckList^Integrity(,dblist,,,5)
do Display^Integrity()
kill ^IRIS.TempIntegrityOutput(+$job)


メリット:エントリポイントを用いる場合、maxprocを指定することでパフォーマンスの調整が可能、
     再チェックを行わないので余分なチェックは行わない(時間短縮)、結果をファイルやターミナルに即座に出力
デメリット:自動再チェックは行わないためエラーの場合は独自に再チェックを行う必要がある、グローバルの選択はできない


enlightened【ご参考】
整合性チェック: 高速化と低速化
整合性チェックのパフォーマンスの調整
整合性チェックの目安の時間や動作状況の確認、途中で停止する方法について

Discussion (0)0
Log in or sign up to continue