【実務・中級編】CICSにおけるEXEC CICS START/RETRIEVEによる非同期処理 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近入った若手が「PL/Iって変数名に予約語がないから、なんでも名前につけられて自由で便利ですね!」なんて呑気に笑っているのを聞いてね、思わず冷や汗が出ちまったよ。

ちょっと待て、と。PL/Iは確かに文脈依存の言語だから、`IF` や `READ` さえも変数名に使える仕様にはなっている。だがな、そんな真似を本番の基幹システムでやったら、コンパイラは通ったとしても、後からコードを保守するプログラマが発狂するか、深夜の障害対応で泣くことになる。自由度が高い=何をやっても許される、じゃないんだ。プロとしての美学と、地獄を見ないための防衛策が必要なのさ。

さて、前置きはこれくらいにして、今日は現場で一番頭を悩ませるテーマの一つ、CICS環境における `EXEC CICS START` と `RETRIEVE` を使った非同期処理について、骨の髄まで叩き込んでやろう。

バッチと違って、オンライン(CICS)の世界じゃレスポンスタイムが正義だ。重たい処理を画面の裏側でそのまま回したら、端末の向こうでユーザーが待ちくたびれて机を叩くことになる。そこで登場するのが、別タスクに処理をぶん投げる「非同期処理」のテクニックというわけだ。

—

1. なぜ今、非同期処理(START/RETRIEVE)なのか?

メインフレームの基幹系でオンラインとバッチが入り交じる中、夜間バッチを待たずに「今すぐ、しかし裏側でこっそり」処理を走らせたい要件は山のようにある。例えば、受注電文を受け取った瞬間に画面を返却し、裏では別タスクで在庫引き当てや外部システムへの連携データ作成を行うようなケースだ。

ここでCICSの `EXEC CICS START` コマンドを使う。
親タスクから子タスクを非同期に起動し、その際にデータを渡す。子タスク側では `EXEC CICS RETRIEVE` を使ってそのデータを受け取る。

この一連のメカニズム、言葉で聞くと簡単だが、PL/Iで実装する際にはデータ長(LENGTH)の管理や、コミット・同期点の概念をしっかり押さえておかないと、データ化けやタスク迷子の原因になる。現場のベテランが書いた実用コードを見ながら、その急所を解説していこう。

—

2. 実践!PL/Iによる非同期処理のコードリーディング

まずは、親タスク側からデータ付きで子タスクを起動するプログラムと、受け取る子タスク側のプログラムの骨格だ。もちろん、我が国の金融・流通の現場の流儀に従い、すべて大文字、かつ適切なインデントと日本語コメントを添えてある。

親タスク側:トランザクションを飛ばす側

1
/ ========================================================== /
/ 親プログラム:オンライン要求を受け取り非同期タスクを起動 /
/ ========================================================== /
SNDPROG: PROC OPTIONS(MAIN);

DCL 1 WK-COM-AREA,
5 WK-USER-ID CHAR(8),
5 WK-DATA-KEY CHAR(10),
5 WK-STATUS CHAR(2);

DCL 1 EIBK EXT; / CICS EXECINTERFACE BLOCK /

/ 送信データの値を設定 /
WK-USER-ID = ‘SYSADMIN’;
WK-DATA-KEY = ‘A123456789′;
WK-STATUS = ’01’;

/ 非同期タスクの起動(DATAオプションでデータを引き渡す) /
EXEC CICS START
TRANID(‘SUBT’) / 起動するトランザクションID /
TERMID(EIBTRMID) / 親と同じ端末を関連付け(必要に応じて) /
FROM(WK-COM-AREA) / 渡すデータ領域 /
LENGTH(STG(WK-COM-AREA)) / データの長さ /
RESP(CICS-RESP); / 応答コード /

IF CICS-RESP = DFHRESP(NORMAL) THEN
PUT SKIP LIST(‘ASYNCHRONOUS TASK STARTED SUCCESSFULLY.’);
ELSE
PUT SKIP LIST(‘START FAILED. RESP = ‘, CICS-RESP);

EXEC CICS RETURN;

END SNDPROG;

子タスク側:データを受け取って処理する側

1
/ ========================================================== /
/ 子プログラム:STARTコマンドで渡されたデータを受け取る /
/ ========================================================== /
RCVPROG: PROC OPTIONS(MAIN);

DCL 1 WK-RCV-AREA,
5 RCV-USER-ID CHAR(8),
5 RCV-DATA-KEY CHAR(10),
5 RCV-STATUS CHAR(2);

DCL CICS-RESP FIXED BIN(31);
DCL CICS-DATALEN FIXED BIN(31);

/ STARTコマンドで渡されたデータを取得する /
EXEC CICS RETRIEVE
INTO(WK-RCV-AREA)
LENGTH(CICS-DATALEN)
RESP(CICS-RESP);

/ データの取得判定 /
IF CICS-RESP = DFHRESP(NORMAL) THEN
DO;
/ 正常にデータを受け取った場合の処理 /
CALL PROCESS-DATA;
END;
ELSE IF CICS-RESP = DFHRESP(ENDFILE) THEN
DO;
/ 渡されたデータがもうない場合(通常はRETRIEVEは1回のみだが念のため) /
GOTO TASK-EXIT;
END;
ELSE
DO;
/ 予期せぬエラー /
CALL ERROR-ROUTINE;
END;

TASK-EXIT:
EXEC CICS RETURN;

PROCESS-DATA: PROC;
/ ここでVSAMへの書き込みや重たい計算処理を行う /
PUT SKIP LIST(‘PROCESSING KEY: ‘, RCV-DATA-KEY);
END PROCESS-DATA;

ERROR-ROUTINE: PROC;
PUT SKIP LIST(‘CICS RETRIEVE ERROR OCCURRED. RESP = ‘, CICS-RESP);
END ERROR-ROUTINE;

END RCVPROG;

—

3. 現場で嵌まりやすい「罠」とデバッグのコツ

この `START`/`RETRIEVE` 機構、動かすだけなら簡単だが、本番運用やマイグレーションの場面で必ずと言っていいほどハマるポイントがいくつかある。ベテランからのアドバイスとして3つ挙げておこう。

① 予約語がない言語仕様の落とし穴

冒頭でも言った通り、PL/Iには厳密な意味での「予約語」が存在しない。コンパイラは前後の文脈(Context)で判断する。
例えば、上記のコードで `START` や `LENGTH` といったCICSのキーワードやビルトイン関数に似た名前をうっかり変数名に使ってしまうと、コンパイラはエラーを吐くか、あるいは最悪の場合、意図しない解釈をして誤ったコードを生成する。
自社内のコーディング標準(Coding Standard)で「CICSコマンドやPL/Iの主要なキーワードを変数名に使うな」と厳しく定められているのは、この言語の特性に起因しているんだ。ルールは絶対だと思って守ってくれ。

② LENGTH指定の厳密性

`FROM(WK-COM-AREA)` で渡す際、`LENGTH(STG(WK-COM-AREA))` とPL/Iの `STG`(`STORAGE`)ビルトイン関数を使っている点に注目してほしい。
CICSの世界では、C言語的なポインタやアライメント、文字型(CHAR)の長さがそのままメモリ上のバイト数と一致しないことがある。PL/Iの構造体をそのまま渡す場合、コンパイラが生成するアライメント(境界調整)によってパディング領域が含まれ、親と子で認識するデータ長がズレる事故が稀によく起きる。
構造体を使うときは、必ず `UNALIGNED` 属性を明示するか、`STG` ビルトイン関数で正確なサイズを渡すように徹底することだ。

1
/ 構造体定義のベストプラクティス(UNALIGNEDをつける) /
DCL 1 WK-COM-AREA UNALIGNED,
5 WK-USER-ID CHAR(8),
5 WK-DATA-KEY CHAR(10),
5 WK-STATUS CHAR(2);

この `UNALIGNED` を忘れると、ハードウェアの境界調整のせいで予期せぬパディングバイトが挟まり、子タスク側の `RETRIEVE` でデータがズレてバグの温床になる。これ、テスト環境では発覚しにくくて、本番のデータ量が増えた時に突然爆発する嫌なタイプのバグだから気をつけな。

③ 異常系(RESPコード)のハンドリングをサボるな

「どうせ正常に動くだろう」と `RESP` オプションのチェックを省くプログラマがいるが、メインフレームのエンジニアとしては失格だ。CICSがビジー状態のときや、一時記憶域の容量制限に引っかかったとき、`START` は容赦なく非正常レスポンスを返す。
エラー時は必ずシスログ(SYSLOG)やCICSのCESE(CESEタンポポ・ログ)にメッセージを出力し、ダンプを取得できるように組んでおくこと。

—

まとめ

PL/Iの柔軟な構文と、CICSの強力な非同期処理メカニズムを組み合わせれば、スケーラブルで堅牢なオンラインシステムを構築できる。

言語の自由さに甘えず、コンパイラの挙動の裏側(メモリ配置やアライメント)まで想像力を働かせること。それが、トラブルを未然に防ぎ、次世代の若手に胸を張って引き継ぎができる「本物のメインフレーム・アーキテクト」への第一歩だ。

さて、今日の講義はここまでだ。コーディングに戻るとしようか。

タイトルとURLをコピーしました