検索

クリアフィルター
記事
Hiroshi Sato · 2020年10月19日

LinuxでのKernelパラメータ値の設定

これはInterSystems FAQ サイトの記事です。 一部のKernelパラメータはInterSystems製品を稼働するためには、不適切な値となっているため、調整する必要があります。 1. kernel.shmmax (共有メモリセグメントの最大値) 通常既定値で問題ありません。   設定する場合は、共有メモリにあわせたshmmaxの値は以下の a)~ f) の合計値になります。 a) データベースバッファ globals(Mbytes) x 1.075 globals b) ルーチンバッファ routines(Mbytes) x 1.02 routines c) 一般メモリヒープ gmheap(Kbytes) または core数 x 6(Mbytes)のいずれか大きな値の方 gmheap d) ジャーナル・バッファ jrnbufs(Mbytes) jrnbufs e) ECP (MaxServers + MaxServerConn)*2(Mbytes) MaxServers MaxServerConn f) その他 10Mbytes 2. kernel.shmall (総共有メモリサイズ) 通常既定値で問題ありません。 システムで使用する共有メモリの合計サイズになります。 単位はページ(4Kbytes)です。 この値は少なくとも使用する共有メモリサイズ以上にする必要があります。 3. kernel.shmmni (共有メモリセグメント数) 通常既定値で問題ありません。 使用するセグメント数は1つです。 (データベースバッファ、ルーチンバッファ、一般メモリヒープは1つの共有メモリセグメントに配置されます) ただし、管理ポータル用のApache にてセグメントを1つ使用します。 このため、管理ポータル用Apache起動が有効な場合、1インスタンス当り使用するセグメント数は2になります。 4. kernel.sem (semmni、semmsl、 semmns セマフォ数) 通常既定値で問題ありません。 設定する場合は、プロセステーブルのエントリ数と同数のセマフォを使用します。 プロセステーブルのエントリ数は必要に応じて自動拡張し、初期値の128から32個単位で自動拡張しますが、 その際にセマフォも同じ数使用します。 よって、セマフォのKernelパラメータの設定値の算出のためには、最大プロセス数(デーモンプロセスも含む) を見積もる必要があります。 semmns = 最大プロセス数(32の倍数) + その他InterSystems製品以外で使用するセマフォ数 semmni × semmsl > semmnsになるようsemmniとsemmslの値を設定します。 通常semmslは既定値を使用し semmniの値を変更します。 ※semmni:システム全体のセマフォ集合の最大数。方針依存 。 (Linuxではこの制限値は kernel.sem の第4フィールドに対応し、読み出しも変更もできる。) ※semmsl:semidあたりのセマフォの最大数。 実装依存。 (Linuxではこの制限値は kernel.sem の第1フィールドに対応し、読み出しも変更もできる。) ※semmns:システム全体のセマフォの最大数。方針依存。 (Linuxではこの制限値は kernel.sem の第2フィールドに対応し、読み出しも変更もできる。) semmsl × semmniより大きな値は意味を持たない。 5. vm.nr_hugepages(HugeTLB ページ数または静的なヒュージ・ページ数) 共有メモリを HugeTLB ページまたは静的なヒュージページで使用する場合に設定する必要があります。 HugeTLB ページの使用は、パフォーマンスにおいて非常に有益な為、強く推奨します。 設定値は共有メモリのサイズ(以下の a)~ f) の合計値)をヒュージ・ページのサイズで割った値になります。 a) データベースバッファ globals(Mbytes) x 1.075 globals b) ルーチンバッファ routines(Mbytes) x 1.02 routines c) 一般メモリヒープ gmheap(Kbytes) または core数 x 6(Mbytes)のいずれか大きな値の方 gmheap d) ジャーナル・バッファ jrnbufs(Mbytes) jrnbufs e) ECP (MaxServers + MaxServerConn)*2(Mbytes) MaxServers MaxServerConn f) その他 10Mbytes ヒュージ・ページのサイズは、/proc/memoinfo の Hugepagesize値にて確認します。 Hugepagesize値は、プラットフォームやLinux バージョンにより異なりますのでご注意ください。 Linux でのヒュージ・ページの構成 6. 透過的ヒュージ・ページ(THP)の無効化 Linux 2.6.38 カーネル以降、透過的ヒュージ・ページは既定で有効になっています。 この設定が有効な場合、プロセスの作成頻度が高いアプリケーションではメモリ割り当ての遅延を引き起こすことがあります。 これによりパフォーマンスに影響する可能性がある為、透過的ヒュージ・ページを無効にすることをお勧めします。 無効化の方法はLinuxバージョンにより異なりますのでご注意ください。 LinuxのTransparent HugePageとInterSystems IRISへのインパクト 最新の状況に合わせて記事を大幅更新しました。
質問
Yuji Ohata · 2021年5月18日

AWS CodeBuildを用いた自動ビルドの仕組みについて。

こんにちは、皆さま。業務でIRISを用いて開発を行っている者です。 AWSのcodebuildを利用して、IRIS環境をビルドされた経験がある方がいらっしゃたら情報共有いただきたいです。 ただいま取り組んでいるプロジェクトでは、IRISのclsファイルをaws codecommitのgitリポジトリで管理しております。※Dockerコンテナではない。 ここで作成されたソースコードをaws Codebuildの仕組みを利用して自動でコンパイルチェックをする仕組みを構築したいと考えております。コンパイルを実行する環境としては、AWS ECRに自作したDockerコンテナを利用しております。※コンテナはstore/intersystems/iris-community:2020.1.0.215.0を基底に、簡単なネームスペースを作成しただけのもの。 ローカルPC上ではIRIS環境として利用できることを確認済み。 ビルドはbuildspec.ymlにて、以下のような命令で行おうと考えております。 1. iris start IRIS でirisを起動。2. iris terminal IRISでターミナルにログイン。3. gitから取得したコードをImport & コンパイル。 しかし、上記を実行しようとすると2の時点で Access Deniedのエラーとなってしまいます。 whoamiでユーザ状態を見てみると、rootになっており、irisownerではないためそれが原因かと考えておりますが、su irisownerを実行してもユーザが切り替えられず、手詰まり状態です。 ①そもそもcodebuildを利用する方針として、上記のやり方は正しいのでしょうか?②正しい場合、iris terminalに入るために何かいい方法はないでしょうか? 長文となり恐縮ですが、ご意見いただければ幸いです。 自己解決しました! 以下のように実行することで、irisownerのユーザでIRIS内の命令を直接実行できるようになりました。 su - irisowner -c "iris terminal IRIS \"##CLASS(%SYSTEM.OBJ).ImportDir(\\\"参照パス\\\",\\\"*.cls;*.mac;*.int;*.inc;*.dfi\\\",\\\"ck\\\",,1)\" " 一旦問題は解消されたので課題は閉じさせていただこうと思うのですが、 Codebuildの使い方について他にベストプラクティス等あれば 情報いただけますと幸いです。 Ohataさん、情報共有ありがとうございます。 Codebuild固有の、という事ではないのですが、IRISのイメージはhttps://docs.docker.com/develop/develop-images/dockerfile_best-practices/#userこちらに従い、ビルド時のUSERはirisownerになっています。 $ docker history containers.intersystems.com/intersystems/iris:2020.1.0.215.0 | grep USER <missing> 13 months ago /bin/sh -c #(nop) USER irisowner 0B ですので、そのまま実行した場合は $ docker run --rm -it --entrypoint whoami store/intersystems/iris-community:2020.1.0.215.0 irisowner となります。Dockerfile内にて、パッケージマネージャ(apt)の使用などのために USER root を実行されておられませんでしょうか?その場合、可能な限りは、最終的には USER ${ISC_PACKAGE_MGRUSER} (ISC_PACKAGE_MGRUSERの内容はirisownerです)を実行しておくことをお勧めいたします。この方法でビルドされたイメージはsu -irisownerなしで、IRISにO/S認証でのログインが可能になります。下記は使用例です。 https://github.com/intersystems-community/objectscript-docker-template/blob/08a79bfd2d990cd0f7c5c37d9bba4ffde86bb09c/Dockerfile#L14 もし、なんらかの理由で実行時にrootによるアクセスが必要になる場合は、Docker CLIであれば下記が使用できます。 $ docker exec -ti -u root containerId whoami root Iwamoto さん ご連絡いただきありがとうございます。docker起動時にユーザ指定できるのは知りませんでした!まだまだいろいろなことができそうなので、こちらでも触ってみるようにします。 方法を紹介しておいて、何なのですが...こちらの記事に書いたのですが、Kubernetesの同類のコマンドであるkubectl execには、(今のところ?)このユーザ指定の機能がありません。ブログで見かけるようなdockerのCLI経由でのアクセスも、V1.23以降はできなくなる(Kubernetesでのdocker使用が廃止される)そうなので、もし、将来、運用もコンテナで...という事をお考えでしたら、あまりこの機能に依存しすぎるのは避けておいたほうが無難かもしれません。 CircleCI,Gitlab, GitHub Actionsあたりは例があるのですが、AWS CodeBuildはなぜか見かけません。YAMLを共有いだだければ、いろんなアイデアが集まるかもしれませんよ? 解決策に書いた以上のことはあまり何もしてないのですが、buildspec.ymlの内容を転記させていただきます。 build: commands: - mkdir -p /user/local/work - cd .. - mv * /user/local/work - cd /user/local/work - chmod -R 777 /user/local/work - iris start IRIS - su - irisowner -c "iris terminal IRIS \"##CLASS(%SYSTEM.OBJ).ImportDir(\\\"/user/local/work\\\",\\\"*.cls;*.mac;*.int;*.inc;*.dfi\\\",\\\"ck\\\",,1)\" " | tee result.txt - find result.txt | xargs grep -n "error" | wc -l - | if find result.txt | xargs grep -n "error" | wc -l >0; then false; fi
記事
Toshihiko Minamoto · 2021年7月20日

Windows環境でnginxを構築する

皆さん、こんにちは。 InterSystems製品ではCache 2015.1からNGINXに対応しているのですが、インストールマニュアルを見るとC言語のソースファイルが提供されていて、「むむむ。。。コンパイルか。。。」と躊躇してしまいますね(^_^;)。そこで、実際にWindows環境でNGINXをビルドしてみましたのでレポートします。 IRISのインストール IRISまたはIRIS for Healthのキットを実行し、インストールします。Nginxのアクセスに必要なUniversal Web gateway Moduleをインストールするには、セットアップタイプでCustomを選択し、Web Server Gatewayを明示的にインストールする必要がありますので、ご注意ください。 また、今回はIRIS for Healthをインストールしていますので、C:\InterSystems\IRISHealthディレクトリにIRISをインストールしています。IRISだったり、他のディレクトリにインストールされている場合は適宜インストールディレクトリを読み替えてください。 VisualStudioのインストール Windows環境でコンパイルが必要ですのでVisualStudioをインストールします。バージョンはそんなに古くなければ大丈夫かと思います。ちなみに私はWindows 10の環境にインストールしていたVisual Studio 2017を使用しました。 MSYSのインストール MSYSはSourceForge(https://sourceforge.net/projects/mingw/files/MSYS/)からダウンロードします。以下の画面の「Download Latest Version」ボタンをクリックします。 ダウンロードされた、mingw-get-setup.exeを実行します。 以上でインストールが始まります。 インストールが終わると以下のInstallation Managerが表示されますので、Package欄のmsys-base,msys-bashを選択、「installation」メニューの「update catalogue」をクリックし、これらのパッケージをインストールします。 PATH環境変数の更新 MinGWのコマンドが実行されるようにPATH環境変数を変更します。 Windowsのスタートメニューの「すべてのアプリ」をクリックし、「Windowsシステムツール」にある「コントロールパネル」をクリックします。「システムとセキュリティ」の「システム」メニューをクリックします。以下の画面が表示されますので、「システムの詳細設定」をクリックします。 システムのプロパティが表示されますので、「環境変数...」をクリックします。すると環境変数一覧が表示されますので、システム環境変数のPathをクリックし、「編集」ボタンをクリックします。  以下の画面にて変数値の最後にMinGWのコマンド実行パス「;C:\MinGW\MSYS\1.0\bin\」を追加し、「OK」ボタンで確定します。 Strawberry perlのインストール SSL対応に必要なPerlとして、Strawberry Perl (https://strawberryperl.com)をダウンロードします。今回は最新版のStrawberry-perl-5.32.1.1-64bit.msiをダウンロードしました。ダウンロードしたmsiファイルを実行し、以下の画面のようにインストールしました。     Mercurialクライアントのインストール Mercurial(TurtoiseHg) 5.8.1をサイト(https://www.mercurial-scm.org/)からダウンロードします。以下の画面の赤枠部分をクリックします。 tortoisehg-5.8.1-x64.msiを実行し、インストールを行います。     ライブラリ(PCRE、zlib、OpneSSL)のダウンロード PCREはダウンロードサイト( https://ftp.pcre.org/pub/pcre/pcre-8.45.tar.gz )からダウンロードします。zlibは http://zlib.net/zlib-1.2.11.tar.gz からダウンロードします。Open SSLは https://www.openssl.org/source/openssl-1.1.1k.tar.gz からダウンロードします。 これらのgzipファイルはドキュメントフォルダのnginx_devフォルダに移動しておきます。 ビルド環境用フォルダの作成とソースの配置 ビルドに必要なソフトウェアはインストールできましたので、構築環境を作成していきたいと思います。 まず、ドキュメントフォルダ下にnginx_devフォルダを作成します。 Windowsスタートメニューから「すべてのアプリ」「Visual studio 2017 フォルダ」「VS 2017用 x64 Native Toolsコマンドプロンプト」メニューを選択し、コマンドプロンプトを起動します。 bashを起動します。 > c:\MinGW\MSYS\1.0\bin\bash nginx_devフォルダに移動します。 $ cd $USERPROFILE/Documents/nginx_dev nginxソースをダウンロードMercurialを使用してnginxのサイトからソースコードをダウンロードします。 $ hg clone http://hg.nginx.org/nginx ライブラリ格納用のフォルダを作成します。 $ mkdir objs $ mkdir objs/lib $ cd objs/lib ライブラリの解凍 $ tar -xzf ../../../pcre-8.44.tar.gz $ tar -xzf ../../../zlib-1.2.11.tar.gz $ tar -xzf ../../../openssl-1.1.1k.tar.gz CSPのソースファイルをコピーlibフォルダ配下にcspフォルダを作成し、IRISに含まれるWeb Gatewayコンポーネントのソースファイルをcspフォルダにコピーします。 $ mkdir csp $ cp /c/InterSystems/IRISHealth/dev/csp/nginx/*.[ch] csp cspフォルダにconfigファイルを作成 $ cd csp $ cat > config ngx_addon_name=ngx_http_csp_module_sa HTTP_MODULES="$HTTP_MODULES ngx_http_csp_module_sa" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_csp_module_sa.c" 以上で準備は完了です。 ソースのビルド configureを実行し、ビルドに必要なファイルを生成します。 $ cd ../../.. $ auto/configure \ --with-cc=cl \ --with-debug \ --prefix= \ --conf-path=conf/nginx.conf \ --pid-path=logs/nginx.pid \ --http-log-path=logs/access.log \ --error-log-path=logs/error.log \ --sbin-path=nginx.exe \ --http-client-body-temp-path=temp/client_body_temp \ --http-proxy-temp-path=temp/proxy_temp \ --http-fastcgi-temp-path=temp/fastcgi_temp \ --http-scgi-temp-path=temp/scgi_temp \ --http-uwsgi-temp-path=temp/uwsgi_temp \ --with-cc-opt=-DFD_SETSIZE=1024 \ --with-pcre=objs/lib/pcre-8.45 \ --with-zlib=objs/lib/zlib-1.2.11 \ --with-openssl=objs/lib/openssl-1.1.1k \ --with-openssl-opt=no-asm \ --with-http_ssl_module \ --with-http_auth_request_module \ --add-module=objs/lib/csp nmakeでビルドを行います。 $ nmake -f objs/Makefile これでobjsフォルダに nginx.exe が作成されます。 nginxのインストール nginxのインストールはnginxダウンロードサイト( https://nginx.org/en/download.html ) からzipファイルをダウンロードします。zip ファイルを c:\ に展開すると「nginx-x.x.x」というフォルダが作成されます。 c:\nginx-x.x.xフォルダにある nginx.exe を先ほど作成した nginx.exe で上書きします。 Universal Web gateway moduleの設定 をを作成し、C:\InterSystems\IRISHealth\binフォルダにあるCSPx.dll、CSPxSYS.dllをコピーします。C:\InterSystems\IRISHealth\CSP\binフォルダにもCSPx.dllはありますが、これは32bitバージョンのdllですのでご注意ください。 また、C:\InterSystems\IRISHealth\CSP\binフォルダのCSP.iniをc:\nginx-x.x.x\cspフォルダにコピーします。 コピーが終わりますと、C:\nginx-x.x.x\confフォルダにある、nginx.confを編集します。以下のように http { } 設定ブロックの 中に以下のようにCSPModulePathを追加します。 : #gzip on; CSPModulePath csp/; server { : また、server { } 設定ブロックには以下のセクションを追加します。 server { listen 80; server_name localhost; : location / { root html; index index.html index.htm; } # 以下の部分を追加 location /csp { CSPFileTypes csp cls zen cxw; } : 以上でnginx.confの編集は終了です。nginxを起動します。 > start c:\nginx-x.x.x\nginx 起動に成功すると、以下の画面が表示されますので、アクセスを許可したいネットワークを選択し、「アクセスを許可する」ボタンをクリックします。 以上で、nginxからCSPアクセスできるようになりました。手順が多いですが、ぜひチャレンジしてみてください。 また、ご意見、ご質問等ございましたら、お気軽にコメント欄にお寄せください。よろしくお願いします。
記事
Toshihiko Minamoto · 2021年6月15日

MonCaché - MongoDB としての Caché

**MonCaché — InterSystems Caché での MongoDB API 実装** ![](https://habrastorage.org/files/26f/995/320/26f995320d4a4d5fbe44c8ba3b3fe24d.png) 免責事項:この記事は筆者の私見を反映したものであり、InterSystemsの公式見解とは関係ありません。 ## 構想 プロジェクトの構想は、クライアント側のコードを変更せずに MongoDB の代わりに InterSystems Caché を使用できるように、ドキュメントを検索、保存、更新、および削除するための基本的な MongoDB(v2.4.9) API 機能を実装するところにあります。 ## 動機 おそらく、MongoDB に基づくインターフェースを使って、データストレージに InterSystems Caché を使用すると、パフォーマンスが向上するのではないか。 このプロジェクトは、学士号を取得するための研究プロジェクトとして始まりました。 ダメなわけないよね?! ¯\\_(ツ)_/¯ ## 制限 この研究プロジェクトを進める過程で、いくつかの簡略化が行われました。 * プリミティブ型のデータのみを使用する: _null_、_boolean_、_number_、_string_、_array_、_object_、[_ObjectId_](https://docs.mongodb.com/manual/reference/method/ObjectId/) * クライアント側のコードは MongoDB ドライバーを使って MongoDB と連携する。 * クライアント側のコードでは、MongoDB Node.js ドライバーを使用する。 * クライアント側のコードでは、MongoDB API の基本的な機能のみを使用する: * **[_find_](https://docs.mongodb.com/manual/reference/method/db.collection.find)**、**[_findOne_](https://docs.mongodb.com/manual/reference/method/db.collection.findOne)** — ドキュメントの検索 * **[_save_](https://docs.mongodb.com/manual/reference/method/db.collection.save)**、**[_insert_](https://docs.mongodb.com/manual/reference/method/db.collection.insert)** — ドキュメントの保存 * **[_update_](https://docs.mongodb.com/manual/reference/method/db.collection.update)** — ドキュメントの更新 * **[_remove_](https://docs.mongodb.com/manual/reference/method/db.collection.remove)** — ドキュメントの削除 * **[_count_](https://docs.mongodb.com/manual/reference/method/db.collection.count)** — ドキュメント数のカウント ## 実装 タスクは最終的に次のサブタスクに分割されました。 * 選択した基本機能ように、MongoDB Node.js ドライバーのインターフェースを再作成する。 * このインターフェースを InterSystems Caché を使用してデータストレージ用に実装する。 * Caché でデータベースの表現スキームを設計する。 * Caché でコレクションの表現スキームを設計する。 * Caché でドキュメントの表現スキームを設計する。 * Node.js を使って Caché と対話するためのスキームを設計する。 * 設計したスキームを実装して、少しテストする。 :) ## 実装の詳細 最初のサブタスクは問題ではなかったので、中間の説明を省略して、インターフェースの実装部分を説明します。 MongoDB は、データベースをコレクションの物理的なコンテナとして定義します。 コレクションはドキュメントのセットで、ドキュメントはデータのセットです。 ドキュメントは JSON ドキュメントのようですが、大量の許容型を持っています。つまり BSON です。 InterSystems Caché では、すべてのデータはグローバルに保存されます。 簡単に言えば、階層データ構造として考えることができます。 このプロジェクトでは、すべてのデータは単一のグローバル(^MonCache )に保存されます。 そのため、階層データ構造を使用して、データベース、コレクション、およびドキュメントの表現スキームを設計する必要があります。 Caché におけるデータベースの表現スキーム **実装したグローバルレイアウトは、数多くの潜在的なレイアウトの 1 つに過ぎず、これらのレイアウトには、それぞれにメリットと制限があります。** MongoDB では、1 つのインスタンスに複数のデータベースが存在する可能性があるため、複数の分離されたデータベースを格納できるようにする表現スキームを設計する必要があります。 MongoDB はコレクションをまったく含まないデータベースをサポートしていることに注意しておきましょう(これらを「空」のデータベースと呼ぶことにします)。 私はこの問題に対し、一番単純で一番明白なソリューションを選択しました。 データベースは、^MonCache グローバルの第 1 レベルノードとして表されます。 さらに、そのようなノードは、「空」のデータベースのサポートを有効にするために ”” 値を取得します。 問題は、これを行わずに子ノードを追加するだけの場合、それらを削除すると親ノードも削除されるということです(これがグローバルの動作です)。 したがって、各データベースは Caché 内で次のように表されます。 ^MonCache(<db>) = "" たとえば、「my_database」データベースの表現は次のようになります。 ^MonCache("my_database") = "" ### Caché におけるコレクションの表現スキーム MongoDB は、コレクションをデータベースの要素として定義します。 単一のデータベース内にあるすべてのコレクションには、正確なコレクションの識別に使用できる一意の名前があります。 このことが、グローバルでコレクションを表現する単純な方法を見つけ、第 2 レベルのノードを使用する上で役立ちました。 次に、2 つの小さな問題を解決する必要があります。 1 つ目の問題は、コレクションがデータベースと同じように空であることができるということです。 2 つ目は、コレクションが一連のドキュメントであるということです。 そして、これらのドキュメントはすべて、互いに分離している必要があります。 正直なところ、コレクションノードの値として自動的に増分する値のようなカウンターを使う以外に良いアイデアが浮かびませんでした。 すべてのドキュメントには一意の番号があります。 新しいドキュメントが挿入されると、現在のカウンターの値と同じ名前が付いた新しいノードが作成され、カウンターの値が 1 つ増加するというアイデアです。 したがって、各 Caché コレクションは、次のように表されます。 ^MonCache(<db>) = ""^MonCache(<db>, <collection>) = 0 たとえば、「 _**my_database**_」データベース内の「**_my_collection_**」コレクションは次のように表されます。 ^MonCache("my_database") = ""^MonCache("my_database", "my_collection") = 0 ### Caché におけるドキュメントの表現スキーム このプロジェクトでは、ドキュメントは追加の ObjectID という型で拡張された JSON ドキュメントです。 ドキュメントの表現スキームは、階層データ構造で設計する必要がありました。 いくつかの驚きに遭遇したのはここです。 まず、Caché では「ネイティブ」の null がサポートされていなかったために、使用できなかったことです。 もう 1 つは、ブール値が 0 と 1 の定数で実装されているということです。 つまり、1 が true で 0 が false ということなのです。 一番予想していた問題は、ObjectId を格納する方法を考え出す必要があるということでした。 結局、これらの問題はすべて最も簡単な方法でうまく解決されたのです。というか、解決されたと思いました。 以下では、各データ型とその表現について説明します。 ### Caché のインタラクションスキーム Node.js ドライバーの選択は、InterSystems Caché と連携する上で論理的かつ単純な決定であるように思われました(ドキュメントサイトには、Caché と対話するためのドライバーがほかにも掲載されています)。 ただし、ドライバーの機能では不十分です。 私が行いたかったのは、1 回のトランザクションで複数の挿入を実行することだったので、 Caché 側で MongoDB API をエミュレートするために使用される一連の Caché ObjectScript クラスを開発することにしました。 Caché Node.js ドライバーは、Caché クラスにアクセスできませんでしたが、Caché からプロフラム呼び出しを行うことができました。 このことから、小さなツールが作成されました。ドライバーと Caché クラスを繋ぐ、一種のブリッジです。 結局、スキームは次のようになりました。 ![](https://habrastorage.org/files/38f/e34/6c9/38fe346c94ce4663a234d1e7057110fd.png) プロジェクトの作業を進めながら、NSNJSON(Not So Normal JSON: あんまり普通じゃない JSON)と名付けた、ドライバーを介して ObjectId、null、true、および false を Caché に「密輸」する特別な形式を作成しました。 この形式の詳細については、GitHub の対応するページ(NSNJSON)をご覧ください。 MONCACHÉ の機能 ドキュメント検索には、次の基準を使用できます。 * _[**$eq**](https://docs.mongodb.com/manual/reference/operator/aggregation/eq)_ — 等号 * [**_$ne_**](https://docs.mongodb.com/manual/reference/operator/aggregation/ne) — 不等号 * [**_$not_**](https://docs.mongodb.com/manual/reference/operator/aggregation/not) — 否定 * [**_$lt_**](https://docs.mongodb.com/manual/reference/operator/aggregation/lt) — より小さい(未満) * [**_$gt_**](https://docs.mongodb.com/manual/reference/operator/aggregation/gt) — より大きい * [_**$exists**_](https://docs.mongodb.com/manual/reference/operator/aggregation/exists) — 有無、存在 ドキュメントの更新操作には、次の演算子を使用できます。 * [**_$set_**](https://docs.mongodb.com/manual/reference/operator/update/set) — 値の設定 * [**_$inc_**](https://docs.mongodb.com/manual/reference/operator/update/inc) — 指定された数による増分 * [**_$mul_**](https://docs.mongodb.com/manual/reference/operator/update/mul) — 指定された数による乗算 * [**_$unset_**](https://docs.mongodb.com/manual/reference/operator/update/unset) — 値の削除 * [**_$rename_**](https://docs.mongodb.com/manual/reference/operator/update/rename) — 値の名前変更 ## 例 以下のコードは、私がドライバーの公式ページから取得して少し修正したコードです。 var insertDocuments = function(db, callback) { var collection = db.collection('documents'); collection.insertOne({ site: 'Habrahabr.ru', topic: 276391 }, function(err, result) {     assert.equal(err, null);     console.log("Inserted 1 document into the document collection");     callback(result); });} var MongoClient = require('mongodb').MongoClient  , assert = require('assert'); var url = 'mongodb://localhost:27017/myproject'; MongoClient.connect(url, function(err, db) {    assert.equal(null, err);    console.log("Successfully connected to the server");     insertDocument(db, function() {         db.close();     });}); このコードは、MonCaché と互換性を持つように簡単に変更することができます。 ドライバーを変更するだけです! // var MongoClient = require('mongodb').MongoClientvar MongoClient = require('moncache-driver').MongoClient このコードが実行されると、^MonCache グローバルは次のようになります。 ^MonCache("myproject","documents")=1^MonCache("myproject","documents",1,"_id","t")="objectid"^MonCache("myproject","documents",1,"_id","v")="b18cd934860c8b26be50ba34"^MonCache("myproject","documents",1,"site","t")="string"^MonCache("myproject","documents",1,"site","v")="Habrahabr.ru"^MonCache("myproject","documents",1,"topic","t")="number"^MonCache("myproject","documents",1,"topic","v")=267391 ## デモ ほかのすべてとは別に、小型のデモアプリケーション(ソースコード)を公開しました。また、サーバーの再起動とソースコードの変更を行わないで、MongoDB Node.js から MonCaché Node.js へのドライバーの変更を実演するために、Node.js を使って実装しました。 アプリケーションは、CRUD 演算を製品やオフィスで実行するための小さなツールであり、構成を変更(ドライバーを変更)するためのインア―フェースでもあります。 サーバーでは、構成で選択されたストレージ(Caché または MongoDB)に保存される製品とオフィスを作成できます。 [Orders]タブには、注文のリストが含まれています。 レコードは作成されていますが、フォームは未完成です。 プロジェクトの援護を歓迎しています(ソースコード)。 構成は、Configuration ページで変更できます。 このページには、MongoDB と MonCache の 2 つのボタンがあります。 対応するボタンをクリックすると、希望する構成を選択できます。 構成が変更されると、クライアントアプリケーションはデータソースに再接続します(実際に使用されているドライバーからアプリケーションを分離する概念)。 ## まとめ 結論を出すために、根本的な質問に答えさせてください。 そうです! 基本的な操作におけるパフォーマンスを向上させることができました。 MonCaché プロジェクトは GitHub に公開されており、MIT ラインセンスの下に提供されています。 簡易マニュアル * Caché のインストール * 必要な MonCaché コンポーネントを Caché に読み込む * Caché で MONCACHE 領域を作成する * Caché で、ユーザー名が「**_moncache_**」でパスワードが「**_ehcacnom_**」(**「_moncache_」の逆**)のユーザーを作成する * 環境変数 _**MONCACHE_USERNAME**_ = **_moncache_** を作成する * 環境変数 **_MONCACHE_PASSWORD_** = **_ehcacnom_** を作成する * 環境変数 _**MONCACHE_NAMESPACE**_ = **_MONCACHE_** を作成する * プロジェクトで、依存関係を **_'mongodb'_** から **_'moncache-driver'_** に変更する * プロジェクトを始動! :-) ## InterSystems 教育プログラム InterSystems テクノロジーに基づく独自の研究プロジェクトを始めたい方は、[InterSystems 教育プログラム](http://www.intersystems.com/services-support/learning-services/university-outreach-program/)専用の特設サイトをご覧ください。
記事
Mihoko Iijima · 2021年2月26日

プロセス一覧をプログラムで取得する方法

これは InterSystems FAQ サイトの記事です。 %SYS.ProcessQuery クラスの AllFields クエリを使用すると取得できます。 詳細は、ドキュメント「プロセス(ジョブ)について【IRIS】 / プロセス(ジョブ)について」もご参照ください。 ターミナルでの実行例は以下の通りです。 USER>set stmt=##class(%SQL.Statement).%New() USER>set st=stmt.%PrepareClassQuery("%SYS.ProcessQuery","AllFields") USER>write st // 1が戻ったら%Execute()を実行できます 1 USER>set rset=stmt.%Execute() // Job番号-PID-OSユーザ名-カレントデバイス-ルーチン-状態-ユーザ名 を表示しています。 USER>while rset.%Next() { write rset.%Get("JobNumber"),"-",rset.%Get("Pid"),"-",rset.%Get("OSUserName"),"-",rset.%Get("CurrentDevice"),"-",rset.%Get("Routine"),"-",rset.%Get("State"),"-",rset.%Get("UserName"),!} メソッドで記述した場合は以下の通りです。 ClassMethod ShowJob() { set stmt=##class(%SQL.Statement).%New() set st=stmt.%PrepareClassQuery("%SYS.ProcessQuery","AllFields") set rset=stmt.%Execute() // Job番号-PID-OSユーザ名-カレントデバイス-ルーチン-状態-ユーザ名 を表示します。 while rset.%Next() { write rset.%Get("JobNumber"),$Char(9) write rset.%Get("Pid"),$Char(9) write rset.%Get("OSUserName"),$Char(9) write rset.%Get("CurrentDevice"),$Char(9) write rset.%Get("Routine"),$Char(9) write rset.%Get("State"),$Char(9) write rset.%Get("UserName"),! } }
記事
Hiroshi Sato · 2021年10月12日

%Net.FtpSession クラスを使用してファイルサイズを取得する方法

これはInterSystems FAQ サイトの記事です。ファイルサイズを取得する専用メソッドはありませんが、%Net.FtpSession クラスの sendCommand() メソッドを通してFTPのSIZEコマンドを呼び出し、サイズを取得できます。 簡単な利用例をご紹介します。 まず、以下の様なクラスを定義します。 Class MyNet.FtpSession Extends %Net.FtpSession{ Method getSize(fname As %String, ByRef size As %Integer){set type=..Type // 現Typeを保存しておくdo ..Binary()set st=..sendCommand("SIZE "_fname) //SIZE <file name>set size=..ReturnMessageif type="Ascii" {do ..Ascii()}quit} } そのクラスを利用してファイルサイズを取得する例は以下のとおりです。 set ftp=##class(MyNet.FtpSession).%New() write ftp.Connect(ftpsrv,user,pass) // FTPサイトへ接続ができると1が返ります。 do ftp.getSize(fname,.fsize) // 第2パラメータに指定したファイルのサイズが返ります write "ファイルサイズ : ",fsize,! write ftp.Logout() // 成功したら1が返ります ※ sendCommand() メソッドは [Internal] 属性が付与されているため将来変更される可能性があります。
記事
Hiroshi Sato · 2021年9月29日

プログラミングでタスクスケジュールを登録・参照する方法

これは InterSystems FAQ サイトの記事です。こちらでは、タスクスケジュールを登録・参照するサンプルコードをご紹介します。 ①タスクスケジュール登録のサンプル ※毎日 am1:00に do ^TEST を実行するタスクを作成します。 set task=##class(%SYS.Task).%New() set task.Name="MyTask1" set taskDescription="毎日夜中1:00に^xxx実行" // 任意 set task.NameSpace="USER" set task.TimePeriod=0 set task.DailyFrequency=0 set task.DailyFrequencyTime="" set task.DailyIncrement="" set task.DailyStartTime=$ZTimeh("01:00:00") set task.DailyEndTime="" set task.TaskClass="%SYS.Task.RunLegacyTask" set task.Settings=$LB("ExecuteCode","do ^TEST") // RunLegacyTask の場合は ExecuteCode を設定 write task.%Save() ②タスクスケジュール参照のサンプル  ※タスクスケジュールに登録された内容をプログラムで取得します。 USER>set task=##class(%SYS.Task).%OpenId(1) USER>zwrite tasktask=<object reference="">[14@%SYS.Task]:+----------------- attribute values ------------------| %Concurrency = 1| DailyEndTime = 0| DailyFrequency = 0| DailyFrequencyTime = ""| DailyIncrement = ""| DailyStartTime = 0| DayNextScheduled = 63877| DeleteAfterRun = 0| Description = "毎日零時にジャーナルファイルの切り替えを行います":// 個別に項目を参照したい場合は以下のように行いますUSER>write $ZDT(task.DayNextScheduled)11/21/2015USER>write task.Nameジャーナル切り替えUSER>
お知らせ
Mihoko Iijima · 2022年5月1日

【アーカイブ配信のお知らせ】ストレージの節約手法について(2022年3月9日開催)

開発者のみなさん、こんにちは! 2022年3月9日開催「InterSystems Japan Virtual Summit 2022」のセッション「ストレージの節約手法について」のアーカイブを YouTube に公開いたしました。 (プレイリストはこちら) データベースの構成要素であるデーターベースファイル、ジャーナルファイルに関するストレージ容量の増大を招く要因とその解決策、そしてこれら要素に関わる新機能についてもご説明します。 ストレージのコスト管理が重要なクラウド環境で特に有用な情報です。 ぜひ動画をご参照ください。 【目次】 00:58 ストレージコスト削減に成功されたお客様事例のご紹介 02:24 ディスクの使用状況を詳しく見る 04:57 ストレージコストの節約(=ストレージの節約)のための方法 15:44 ジャーナルのコスト削減(=ストレージの削減) 23:24 今後リリース予定の新機能について
お知らせ
Mihoko Iijima · 2022年5月8日

【アーカイブ配信のお知らせ】データベースミラーリングを使用した HA および DR の構成例(2022年3月9日開催)

開発者のみなさん、こんにちは! 2022年3月9日開催「InterSystems Japan Virtual Summit 2022」のセッション「ミラーリングを使用した HA および DR の構成例」のアーカイブを YouTube に公開いたしました。 (プレイリストはこちら) ミラーリングは、IRIS インスタンス間のデータベースの複製およびフェイルオーバを行う機能です。 動画では、ミラーリングを利用した高可用(HA)なシステムおよびディザスタリカバリ(DR)に対応したシステムの構成例についてご紹介します。 ぜひご参照ください。 【目次】 00:35 ミラーリングの概要 03:20 ミラーリングの種類と機能:フェイルオーバ・ミラー・メンバ 05:07 ミラーリングの種類と機能:非同期ミラー・メンバ 06:33 DR 非同期ミラー・メンバの昇格・降格 07:29 DR 非同期ミラー・メンバの昇格・降格の機能を利用した災害復旧での対応例 10:07 ISCAgent について 11:12 Arbiter について 13:06 ミラーリングの構成例
記事
Megumi Kakechi · 2022年5月25日

設定されているネームスペース一覧をプログラムで取得する方法

これは、InterSystems FAQサイトの記事です。%SYS.Namespace クラスの List クエリで取得することができます。 次のようなルーチンを作成し、ターミナルで実行してください。 1. サンプルの作成 getnsp // ネームスペース一覧を取得する write "nsp:glo:rtn",!,! set statement=##class(%SQL.Statement).%New() set status=statement.%PrepareClassQuery("%SYS.Namespace","List") set resultset=statement.%Execute() while resultset.%Next() { write resultset.%Get("Nsp"),! } quit 2. ターミナルから実行 USER>do ^getnsp%SYSDOCBOOKSAMPLESUSER こちらの記事でご紹介しているクラスクエリを実行する方法は、様々なケースで応用できます。 クラスリファレンスをご覧いただくと、様々なクラスクエリをご覧いただけます。例えば、 %SYS.DatabaseQuery:GetFreeSpace() データベースの空き容量 %SYS.GlobalQuery:DirectoryList  データベース内のグローバル名一覧 %SYS.GlobalQuery:Size  データベース内のグローバルサイズ一覧 %SYS.ProcessQuery:SS  プロセス情報(^%SSユーティリティで確認できる一覧と同じ)などがあり、様々な情報を取得できます。 他にもいろいろございますので、是非ご活用ください。
お知らせ
Seisuke Nakahashi · 2022年6月28日

IRIS 2022.1 日本語ドキュメント公開

このたび InterSystems IRIS 2022.1 の日本語ドキュメントが完成しました。以下のURLでご参照いただけます。 IRIS 2022.1 IRIS for Health 2022.1 Health Connect 2022.1 日本語ドキュメント一覧ページ ランチャーから日本語ドキュメントを参照したい IRIS for Windows では、以下の設定により、ランチャーの [ドキュメント] をクリックしたときに、上記の日本語ドキュメントを表示させることが可能となります。 <IRIS installdir>\httpd\conf\httpd-doc.conf をテキストエディタで開き、Redirect先のURL(右側)を、日本語ドキュメントのもに書き換えて保存する。 具体的には、URLの最後 docbook を、以下のように docbookj に変更する。 IRISを再起動する これにより、ランチャーから、IRIS 2022.1 の日本語ドキュメントが表示されるようになります。 ぜひ日本語ドキュメントご活用いただき、IRIS 製品を便利にお使いいただけると幸いです。
記事
Hiroshi Sato · 2025年4月1日

IRISから外部のWebAPIを呼び出したとき、レスポンス内の全角文字が化ける

これは InterSystems FAQ サイトの記事です。 Content-Typeにcharset 情報が含まれていない場合、IRIS/Cachéは文字コードを判断できず文字コード変換が行われません。 そこで、以下のように、「自動的な文字変換を行わず、UTF-8に変換する処理を記述」することで、その指定がある無しに関わらず、対応することができます。 // requestオブジェクトは以下のように生成  // 詳細処理は省略 // set request=##class(%Net.HttpRequest).%New() // リクエスト時に、文字変換を行わないように指定 set request.ReadRawMode=1 do request.Send("POST",URL) // 受取ったデータを、UTF-8に変換してから、JSON変換 set response = request.HttpResponse.Data set data = response.Read() set data2 = $zcvt(data,"I","UTF8") set response = {}.%FromJSON(data2) write response.%ToJSON() 長い文字列が想定される場合には、一度のReadで読み込めない可能性があるため、以下のようなコーディングを行う必要があります。 set stream=##class(%Stream.TmpCharacter).%New() while 'response.AtEnd { set data = response.Read() set data2 = $zcvt(data,"I","UTF8") do stream.Write(data2) } set res = {}.%FromJSON(stream) write res.%ToJSON()
記事
Mitsuru Amano · 2021年2月25日

VSCODEの統合ターミナルにIRISターミナルを追加する

はじめまして、皆さん。windows telnetを使えば、VSCODEの統合ターミナルでIRISターミナルを使うことも出来ますが、ストレス無く使えるとは言い難い状態でした。 色々調べた結果、telnetよりは快適にIRISターミナルをVSCODEで使う設定が出来たので投稿します。 (※大量のキー連打や、大量表示で文字欠落がたまにあるけど、普通に使うなら大丈夫な感じです) 具体的には、powershellターミナルを文字コード:UTF-8に変更しIrisdb.exeを起動する様に設定するだけでOKです。 1.VSCODEの設定を開く 2.setting.jsonを開く 3.setting.jsonの最後に設定を追加する。 追加するテキスト "terminal.integrated.shellArgs.windows": [ // powershellの引数 "-NoExit", // コマンド実行しても終わらないよ的なオプション "-Command", // コマンドを実行しますよオプション "chcp", // 文字コードを指定するコマンド "65001;", // UTF-8 "C:\\InterSystems\\IRIS\\bin\\irisDb.exe", // irisdb.exeを実行 "-s", // irisインスタンスを指定するオプション(インスタンスが1つの時は不要) "C:\\InterSystems\\IRIS\\mgr" // irisインスタンスディレクトリ(インスタンスが1つの時は不要) ], ※Irisdb.exeとmgrフォルダパスは、IRISのインストールディレクトリのpathに合わせて下さい。 4.IRISターミナルを開く 5.IRISターミナルで日本語入力してみる。 今回の方法は、IRISがローカルにインストールされていることが前提になります。 また、Irisdb.exeをCLIとして使用しても良いのか、IRISのマニュアルで確認していませんので、自己責任で使用して下さい。 Amanoさん 投稿ありがとうございます。 ターミナルからの起動ではないので、エスケープシーケンス等正しく表示されない可能性はありますが、Vscodeでの簡単な設定で、ルーチンやシステム関数の戻り値などをちょっと試すのに便利ですね。 Minamotoさん そうなんです。エスケースシーケンスはダメでした。「USER>W #」で画面をクリアすると、行ズレも発生してしまいますが、デバック目的でのプロラム実行や、バッチ処理の実行は簡単に出来るので重宝しそうです。
記事
Henrique Dias · 2020年12月25日

実行中のiris-analytics-package

皆さん、こんにちは。 iris-analytics-パッケージには、「企業が自社のソフトウェアで InterSystems Analytics の サポートをいかに簡単、そしてシンプルに利用できるかを示す」意図があります。 新しいシンプルなソリューションを作成したり、OpenExchange を使用して既存のソリューションを改善したりすることもできます。 InterSystems IRISにアップグレードしている企業のほとんどは、ツールが提供するすべての機能を活用しています。 このコンテストでの私のもう一つのターゲットは、インターシステムズを長く利用していても、自分たちがアクセスしている可能性をフルに活用していない企業です。 このプロジェクトは、他のプロジェクトをベースにしてインスピレーションを得て作成しました。ありがとう @Evgeny.Shvarov @Guillaume.Rongier7183 @Peter.Steiwer DeepSeeWeb csvgen csvgen-ui AnalyzeThis それらのプロジェクトが一緒になって、このウィザードになった。 インポートウィザード メインページはシンプルで、そこに至るまでのプロセスがわかりやすい。 ウィザードを適切に使用するためのフィールドがいくつかあります。 CSV ファイルを選択します。 セパレータ クラス名 キューブを作成するかどうかを選択します。 キューブ名 ウィザードでダッシュボードのサンプルを作成するかどうかを選択します。 作成したものを可視化するには、DeepSeeWeb を使用します。 デモ 下のGIFは全体の処理を示しています。アップロードボタンをクリックした後、フリーズしているように見えるかもしれませんが、これはレコードがたくさんあるからです。 選択したCSVファイルは15Mb以上あり、134.100件のレジスタをインポートしています。 ちなみに、このデモのデータセットは以下のサイトから入手しました。 The Humanitarian Data Exchange - Find, share and use humanitarian data all in one placeCSVファイルは iris-analytics-package/dataフォルダ内にあります。 あなたがソフトウェアを気に入って、私があなたの投票に値すると思うなら、iris-analytics-packageに投票してください! https://openexchange.intersystems.com/contest/current
記事
Toshihiko Minamoto · 2021年1月11日

データ変更の追跡 - 監査ログ - (2/2)

[前回の記事](https://jp.community.intersystems.com/node/483691)では、データの変更を簡単に記録できる方法をお見せしました。 今回は、監査ログが記録されるデータ構造と監査データを記録する「Audit Abstract クラス」を変更しました。 また、データ構造は親構造と子構造に変更し、それぞれに「トランザクション」とそのトランザクションで「その値によって変更されたフィールド」を記録するテーブルが 2 つ設けられます。 新しいデータモデルをご覧ください。 「監査クラス」から変更したコードをご覧ください。 Class Sample.AuditBase [ Abstract ]{Trigger SaveAuditAfter [ CodeMode = objectgenerator, Event = INSERT/UPDATE, Foreach = row/object, Order = 99999, Time = AFTER ]{          #dim %compiledclass As %Dictionary.CompiledClass          #dim tProperty As %Dictionary.CompiledProperty          #dim tAudit As Sample.Audit          Do %code.WriteLine($Char(9)_"; get username and ip adress")           Do %code.WriteLine($Char(9)_"Set tSC = $$$OK")           Do %code.WriteLine($Char(9)_"Set tUsername = $USERNAME")          Set tKey = ""           Set tProperty = %compiledclass.Properties.GetNext(.tKey)          Set tClassName = %compiledclass.Name          Do %code.WriteLine($Char(9)_"Try {")          Do %code.WriteLine($Char(9,9)_"; Check if the operation is an update - %oper = UPDATE")           Do %code.WriteLine($Char(9,9)_"if %oper = ""UPDATE"" { ")           Do %code.WriteLine($Char(9,9,9)_"Set tAudit = ##class(Sample.Audit).%New()")           Do %code.WriteLine($Char(9,9,9)_"Set tAudit.Date = +$Horolog")          Do %code.WriteLine($Char(9,9,9)_"Set tAudit.UserName = tUsername")          Do %code.WriteLine($Char(9,9,9)_"Set tAudit.ClassName = """_tClassName_"""")          Do %code.WriteLine($Char(9,9,9)_"Set tAudit.Id = {id}")          Do %code.WriteLine($Char(9,9,9)_"Set tSC = tAudit.%Save()")           do %code.WriteLine($Char(9,9,9)_"If $$$ISERR(tSC) $$$ThrowStatus(tSC)")           Do %code.WriteLine($Char(9,9,9)_"Set tAuditId = tAudit.%Id()")           While tKey '= "" {                    set tColumnNbr = $Get($$$EXTPROPsqlcolumnnumber($$$pEXT,%classname,tProperty.Name))                     Set tColumnName = $Get($$$EXTPROPsqlcolumnname($$$pEXT,%classname,tProperty.Name))                     If tColumnNbr '= "" {                              Do %code.WriteLine($Char(9,9,9)_";")                              Do %code.WriteLine($Char(9,9,9)_";")                              Do %code.WriteLine($Char(9,9,9)_"; Audit Field: "_tProperty.SqlFieldName)                               Do %code.WriteLine($Char(9,9,9)_"if {" _ tProperty.SqlFieldName _ "*C} {")                               Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField = ##class(Sample.AuditField).%New()")                               Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.Field = """_tColumnName_"""")                              Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.OldValue = {"_tProperty.SqlFieldName_"*O}")                              Do %code.WriteLine($Char(9,9,9,9)_"Set tAuditField.NewValue = {"_tProperty.SqlFieldName_"*N}")                               Do %code.WriteLine($Char(9,9,9,9)_"Do tAuditField.AuditSetObjectId(tAuditId)")                               Do %code.WriteLine($Char(9,9,9,9)_"Set tSC = tAuditField.%Save()")                               do %code.WriteLine($Char(9,9,9,9)_"If $$$ISERR(tSC) $$$ThrowStatus(tSC)")                               Do %code.WriteLine($Char(9,9,9)_"}")                     }                     Set tProperty = %compiledclass.Properties.GetNext(.tKey)          }          Do %code.WriteLine($Char(9,9)_"}")          Do %code.WriteLine($Char(9)_"} Catch (tException) {")                    Do %code.WriteLine($Char(9,9)_"Set %msg = tException.AsStatus()")                    Do %code.WriteLine($Char(9,9)_"Set %ok = 0")                    Do %code.WriteLine($Char(9)_"}")                    Set %ok = 1}} Test() クラスメソッドを使ってデータを変更することで、Audit クラス (Sample.Audit) から「parent record」が見えるようになり、「children fields」も「Audit Field」クラスから変更されていることが分かります。 (Sample.AuditField)   d ##class(Sample.Person).Test(1)INSERT INTO Sample.Person (Name, Age) VALUES ('TEST PARENT-CHILD', '01')SQLCODE: 0ID Age Name1 01 TEST PARENT-CHILD1 Rows(s) AffectedUPDATE Sample.Person SET Name = 'INTERSYSTEMS DEVELOPER COMMUNITY', Age = '100' WHERE Name = 'TEST PARENT-CHILD'SQLCODE:0ID Age Name1 100 INTERSYSTEMS DEVELOPER COMMUNITY1 Rows(s) Affected 監査クラス:  Sample.AuditField の記録には、監査フィールド = 1 を使った Sample.Audit クラスへの参照があります。 以下に示すように、両クラスの関係を使えば、データに対してクエリを実行できます。  これで完了です。 結果として、最初とは異なるログデータ構造ができました。