メニューボタン
IBMi海外記事2016.02.09

DB2 for i の「ジョブ終了」条件を検出する

Michael Sansoterra 著

こんにちは、マイク:

ETL プロセスは、 ERP システムからデータを抽出し、私たちのビジネス・インテリジェンス・システムに配置しています。ETL プロセスは、中にあるサブシステムをシャットダウンする「1 日の終わり」プロセスを除き、1 日 24 時間 週 7 日稼働しています。実行している SQL プロセスは最終的に変死して、親プロセスは (始動時に復旧せずに) 失敗します。通常終了は RETURN ステートメントを発行するぐらい簡単でしょう。SQL はシャットダウン・プロセスを検出できるでしょうか。

--D.S.

ANSI SQL 規格は、ホストのシャットダウン・プロセスの類を実装する具体的なガイドラインは提供していないため、DB2 for i において IBM が提供してくれた他の特殊な OS 関連のビューや関数に依存して、ホスト OS アクティビティーを調べる必要があるでしょう。これには、ジョブまたはサブシステムを制御しながらシャットダウンするという要求があります。

知らない人にとっては、SQL ジョブ (またはその親サブシステム) を終了するよう要求し、OS によりジョブが強制終了される前に自らシャットダウンする猶予期間がある程度を与えられます。IBM i では、これを「制御された」終了と呼びます。例えば、ENDJOB *CNTRLD コマンドを発行した場合、ジョブが自らシャットダウンするまでのデフォルト時間は 30 秒です。PWRDWNSYS *CNTRLD の場合、デフォルト時間は 3600 秒 (1 時間) です。

DB2 for i サービス・ビューとユーザー定義表関数 (UDTF) を考えるとき、この作業を完全に SQL で達成できるという希望をもたらす OS 関連のサービスが 2 つあります。これらのサービスを使用するには、最新のデータベース・グループの PTF とともに IBM i 7.1 または 7.2 を使用している必要があります。

思い浮かんだ最初のサービスは GET_JOB_INFO UDTF で、現在のジョブのステータスを戻す場合に使用できます。

SELECT V_ACTIVE_JOB_STATUS
INTO @STATUS
FROM TABLE(GET_JOB_INFO('*')) job;

私は、制御されたジョブの終了中に V_ACTIVE_JOB_STATUS 列が「END」を戻すことを望んでいましたが、悲しいことに、制御された終了が要求されても相変わらず値「RUN」を戻していました。もし仮にジョブのステータスが END に変わっても、制御された終了要求を検出したり、それを実行することが信頼できなくて心配になります。

制御された終了ジョブまたは終了サブシステムを要求した場合に、ジョブ・ログにメッセージが保存されることがわかっているので、次に試したのは QSYS2.JOBLOG_INFO UDTF を使用して、制御された終了を要求したときに、特定の「終了ジョブ」メッセージのジョブ・ログを読み取ることでした。

以下のコード・スニペットは、ジョブの終了に関連したメッセージ ID を検出するまで、ジョブ・ログを絶えずループで読み取る、動的コンパウンド・ステートメントです。コードが探す特定のメッセージ ID は次のとおりです。

  • CPC1206 - サブシステムが制御されながら終了中
  • CPC1126 - ジョブ &3/&2/&1 がユーザー &4 により終了

現在のジョブのジョブ・ログでこれらいずれかのメッセージが検出されると、QSYSOPR にメッセージが送信され、終了します。

BEGIN

DECLARE @STATUS CHAR(4);
DECLARE @EXIT CHAR(1) NOT NULL DEFAULT 'N';

WHILE @EXIT='N' DO
IF EXISTS (
/* 制御された終了要求のジョブ・ログを確認 */
SELECT *
FROM TABLE(QSYS2.JOBLOG_INFO('*')|) JL
WHERE MESSAGE_ID IN ('CPC1206','CPC1126')-- Subsystem Ending, Job Ended
AND MESSAGE_TYPE='COMPLETION') THEN
CALL QCMDEXC(
'SNDMSG MSG(''WARNING:SQL Code is taking a dive!'')TOUSR(*SYSOPR)');
SET @EXIT='Y';
ELSE
CALL QSYS2.QCMDEXC ('DLYJOB (1)'); /* シャットダウンを待機 */
END IF;

END WHILE;

END;

アスタリスク・パラメーター (赤色表示) は、 UDTF が現在のジョブのジョブ・ログを読み取る必要があることを表します。メッセージ ID CPC1206 および CPC1126 を見つければ、コマンド ENDSBS、ENDSYS、PWRDWNSYS、ENJOB により制御されたシャットダウンが正常に行われると思います。以下のようなメッセージがないか SQL コードで確認できる、他の「終了ジョブ」条件が存在する場合があります。

  • CPC1209--Controlled End Prestart Jobs (ENDPJ) コマンドの発行
  • CPC1231--ENDJOB がジョブ &3/&2/&1 に対して開始

制御された終了要求が検出された場合、システム・オペレーター・メッセージ・キューに以下が表示されます。

Additional Message Information

From .........: MIKE
Date sent ......: 10/01/15 Time sent ......: 19:33:51

Message ....: WARNING:SQL Code is taking a dive!

これには、プロシージャーが正常シャットダウンするには、シャットダウン条件を確認するコードを、割り当てられた時間に実行する必要があり、そうしないとすべて水の泡になるという警告があります。この技法による予防措置に注意してください。SQL ジョブにたまたま巨大なジョブ・ログがあった場合、この UDTF の動作にはしばらく時間がかかります。もしジョブが自らシャットダウンするのに 10 秒かかる場合、JOBLOG_INFO UDTF に 17 秒間かかると困ったことになります。

RPG ソリューションの使用に関して、私は、非決定的 RPG 外部関数を実装して %SHTDN BIF の値を戻す方が、上記のサンプルよりはるかに軽量なソリューションであり、簡単に実装できると考えています。%SHTDN に慣れていない人には、ジョブがアクティブの場合は「0」、ENDSBS、ENDSYS、PWRDWNSYS、ENJOB いずれかのコマンドにより制御された方法で終了する要求をジョブが保留している場合は、「1」が戻されるだけです。

これを実施する RPG サービス・プログラムは簡単に作成できます。このサンプル・プログラムの名前は JOBEND です。

// モジュールとしてコンパイル
// CRTSRVPGM SRVPGM(zzz/JOBEND) EXPORT(*ALL)
HNoMain
DIsJobEnding PR 1
PIsJobEnding B Export
DIsJobEnding PI 1
/Free
Return %ShtDn;
/End-Free
PIsJobEnding E

サービス・プログラムがビルドされたら、外部 UDF ラッパーを作成します (zzz をライブラリーに置き換えます)。

CREATE OR REPLACE FUNCTION zzz.IsJobEnd()
RETURNS CHAR(1)
LANGUAGE RPGLE
EXTERNAL NAME 'zzz/JOBEND(ISJOBENDING)'
PARAMETER STYLE GENERAL
RETURNS NULL ON NULL INPUT
NOT DETERMINISTIC
NO EXTERNAL ACTION
DISALLOW PARALLEL

関数は非決定的とマークしてください。これで「1」(現在のジョブ終了) または「0」(現在のジョブがアクティブ) を戻す UDF ができました。

VALUES (zzz.IsJobEnd());

この UDF には、RAISE_ERROR 組み込み関数と合わせて使用すると、長期実行照会 (BI ETL 照会など) の解釈に使用できるという利点もあります。

SELECT COL1, COL2...
FROM IMMENSE_QUERY
WHERE CASE WHEN zzz.IsJobEnd()='1'
THEN RAISE_ERROR('38E00',
'Controlled Shutdown Request was Received')
ELSE 'OK' END='OK'

RAISE_ERROR は、エラーをスローするよう設計された不思議な関数です。上記の CASE ステートメントにラップされると、IsJobEnd UDF が「1」と評価される場合、この関数はエラーをスローするだけです。この例では RAISE_ERROR は SQLSTATE 38E00 (ユーザー定義/カスタム SQL 状態) というエラー信号を送り、ステートメントが停止します。動作しているプロセスに関係なく、ステートメント (ストアード・プロシージャー、Java 接続など) は、 SQLSTATE 38E00 という「ハード」エラーを受信し、通常どおりエラーを処理するはずです。

しかし、この技法でも全く効果がない場合があります。(WHERE 節の評価の前) 制御された終了時間中に DB2 がその JOIN 処理を行っている場合、ステートメントは思い通りにシャットダウンしない場合があります。最初に上記のステートメントをテストしたとき、巨大な CROSS JOIN 照会を作成し、ジョブ終了まで 30 秒間指定しても、ジョブは、 IsJobEnd/Raise_Error 式の評価前に OS によって終了しました。

この修復のため、最終的に CROSS JOIN を LEFT JOIN に変更し、JOIN の相関関係において CASE ステートメントを複製しました。

LEFT JOIN MY_BIG_TABLE ON
CASE WHEN dev.IsJobEnd()='1'
THEN RAISE_ERROR('38E00',
'Controlled Shutdown Request was Received')
ELSE 'OK' END='OK'

これは照会には負担ですが、照会処理のさまざまな段階を通して関数を評価するチャンスが DB2 に与えられるということでもあります。

DB2 for i が上記のようなステートメントで動作中に IsJobEnd を常に再評価するよう、IsJobEnd を非決定的として定義することが重要です。最後に、ジョブまたはサブシステムが *IMMED (即時) で終了した場合、このコードが正常に動作しているとは期待しないでください。

あわせて読みたい記事

PAGE TOP