質問
· 2022年12月28日

インスタンスの起動イベントをクラスで拾いたい。

こんにちは、皆さま。
業務でIRISを用いて開発を行っている者です。

どなたかインスタンスの起動を%ZSTART等のMACではなく、
クラスのコールバック等で検知する方法をご存知ではないでしょうか?

背景としましては以下の通りです。

===================================================================

現在、Interoperabilityを利用する機能を開発しております。
Ens.Productionを継承したクラスの中でOnStart()をoverrideしており、
その中で上記のクラス自身をリコンパイルする処理が含まれております。

OnStart()でリコンパイルが行われる際、プロダクションの定義が変更されていると
そのプロセスでのプロダクションの起動が失敗してしまいます。
(プロダクション上の定義と、実際のクラスの状態に不一致が発生するため。)

そのため、クラス自身のリコンパイルをプロダクションの開始時ではなく、
インスタンスの開始時にしたいのですが、%ZSTART等のMACファイルは今回利用したくありません。

なんとかそのイベントをクラスで拾いたいのですが、ご存じな方は情報を頂けますと幸いです。

Product version: IRIS 2022.2
ディスカッション (6)1
続けるにはログインするか新規登録を行ってください

Ohataさん
あけましておめでとうございます。本年もよろしくお願いします。

インスタンス起動時に実行されるメソッドですが、モニタークラスを作成し、Startメソッドに必要な処理を記載するのはいかがでしょうか?

1.プロダクションを設定されているネームスペースにて、以下のような%SYS.Monitor.AbstractSensorを継承したクラスを作成し、Start()メソッドに起動時の処理を記述します。

/// 起動処理サンプル
Class MonTest.Test Extends %SYS.Monitor.AbstractSensor
{
  Method Start() As %Status
  {
    // todo: ここに処理内容を記述します
    do ##class(%SYS.System).WriteToConsoleLog("startup test sensor in "_$namespace)
    Q $$$OK
  }
  Method GetSensors() As %Status
  {
    Quit $$$OK
  }
}

2.ターミナルを起動し、プロダクションを設定しているネームスペースに移動、システムモニター管理(^%SYSMONMGR)を起動します。

USER> zn "PROD"
PROD> do ^%SYSMONMGR

メニューにて 3 (Configure System Monitor Classes) を選択します。

さらに、1 (Configure System Monitor Components) を選択します。

2 (Add Class)を選択し、「Class?」に対してモニタークラス(この例ではMonTest.Test)を入力します。

Enterキーを何度か押し、システムモニター管理を終了します。

3.ターミナルにて%SYSネームスペースに移動し、システムモニター管理を再度起動します。

PROD> zn "%SYS"
%SYS> do ^%SYSMONMGR

メニューにて 3 (Configure System Monitor Classes) を選択します。

さらに、2 (Configure Startup Namespaces) を選択します。

2 (Add Namespace)を選択し、「Namespace?」に対してプロダクションを設定しているネームスペースを入力します。

Namespace? PROD

何度かEnterキーを押して、システムモニター管理を終了します。

4.IRISを再起動します。
 

以上で、起動時にMonTest.TestクラスのStart()メソッドが実行されるかと思います。


注意点としましてはプロダクションを起動するプロセスとは別のプロセスで実行されますので、プロダクションの自動起動は設定せずに、Startメソッドからプロダクションを起動(Ens.DirectorクラスのStartProductionメソッド)したほうが良いかと思います。
 

Minamoto さん

あけましておめでとうございます!
今年もよろしくお願いいたします。

ご回答いただきありがとうございます。

記載いただいた内容はネームスペースの起動を監視できるという所で、
私の要望していた通りのものかと思いますので、一度実験してみようと思います。

また参考までにお伺いしたいのですが、この監視機能の中で、
%ZMIRRORの代替えとして利用できるものはありそうでしょうか…?

%ZMIRROR…プライマリとしての起動時のみ呼ばれる。セカンダリがプライマリに切り替わったタイミングでも呼ばれる。

Ohataさん、こんにちは。
監視機能の中でMirroringの起動時に実行されるメソッドはないかと思います。
ただ、GetSensors()メソッドはデフォルトで30秒ごとに呼ばれますので、$SYSTEM.Mirror.IsPrimary()メソッドの戻り値が0から1になった時点でメソッドを実行することは可能です。
メソッドが呼ばれるタイミングは切り替わった時点よりも遅くなりますが。。。

/// 起動処理サンプル
Class MonTest.Test Extends %SYS.Monitor.AbstractSensor
{ 
Property Primary As %Boolean; 

Method Start() As %Status
{
  // todo: ここに処理内容を記述します
  do ##class(%SYS.System).WriteToConsoleLog("startup test sensor in "_$namespace) 

  // ミラーの状態を保存
  set ..Primary=$SYSTEM.Mirror.IsPrimary() 
  Q $$$OK
} 
Method GetSensors() As %Status
{
  set ret=$$$OK
  set status=$SYSTEM.Mirror.IsPrimary()
  // 状態が変更されたとき
  if ..Primary'=status {
      set ..Primary=status
      if status {
          // Primaryに変更されたとき
          set ret=..BecomePrimary()
      }
  }
  Quit ret
} 
Method BecomePrimary() As %Status
{
  Quit $$$OK
} 
}

Minamoto さん

ご回答いただきありがとうございます!
やはりイベントを直接拾うことは難しそうですね。

定時頂いた案も利用できるとは思うのですが、
%ZMIRRORの代替えとはいかなさそうなので、方針含め考え直してみます。

ありがとうございました!

Ohataさん、おはようございます。
ちなみに、私はモジュール単位で独立して起動時や終了時の処理を実行できるようにしたかったので、%ZSTARTや%ZSTOPルーチンは管理クラスから生成するようにしています。
プログラムはこんな感じです。

/// ZSTARTルーチンの生成
/// 起動時やログイン時、プロセス起動時に実行するプログラムを登録し、
/// %ZSTARTルーチンを生成させる
///         systemcode ... システム起動時に実行するコード
///         logincode ... ログイン時に実行するコード
///         jobcode ... プロセス起動時に実行するコード
///
///  ※複数行にわたるときはCRLFを挿入する
///  モジュール名は%ZLOADERでファイル名から生成されたモジュール名%moduleの値を使用する
ClassMethod GenerateZSTART(systemcode As %String = "", logincode As %String = "", jobcode As %String = "") As %Status
{
    if $get(%module)="" {
        quit $$$ERROR(5001,"モジュール名%moduleが定義されていません")
    }
    kill ^%ZModule.Code("ZSTART","SYSTEM",%module)
    kill ^%ZModule.Code("ZSTART","LOGIN",%module)
    kill ^%ZModule.Code("ZSTART","JOB",%module)

    set:systemcode'="" ^%ZModule.Code("ZSTART","SYSTEM",%module)=systemcode
    set:logincode'="" ^%ZModule.Code("ZSTART","LOGIN",%module)=logincode
    set:jobcode'="" ^%ZModule.Code("ZSTART","JOB",%module)=jobcode

    set rtn=##class(%Routine).%New()
    set rtn.RoutineName="%ZSTART.MAC"
    set sec="" for {
        set sec=$order(^%ZModule.Code("ZSTART",sec))
        quit:sec=""
        do rtn.WriteLine(sec_"() public {")
        set mod="" for {
            set mod=$order(^%ZModule.Code("ZSTART",sec,mod),1,code)
            quit:mod=""
            do:code'="" rtn.WriteLine(code)
        }
        do rtn.WriteLine("}")
    }
    quit rtn.Compile()
}

便宜上、モジュール名は%付き変数で渡していたり、グローバルを使っていたりしますが、
モジュール名と起動、停止コマンドを入れるテーブルを作成し、トリガーで項目が更新される度にルーチンを生成するメソッドを実行しても良いかと思います。

ご参考まで。

確かにこれであれば、プログラム管理上に.MACが現れることもないので、
.CLSに絞った状態で管理できそうですね!

参考にさせていただきます。
ありがとうございます!