【テクニカル・上級編】ENVIRONMENT属性のVSAM/KSDS/ESDS/RRDSの指定とアクセス特性 – PL/Iの基本構文とデータ制御実践ガイド

メインフレームの心臓部を握る VSAM と PL/I の深層

ようこそ、レガシーシステムの荒海へ。
現代のモダンなクラウドネイティブ環境に慣れ親しんだエンジニアから見れば、IBMメインフレームの世界は「ブラックボックスの塊」に映るかもしれない。だが、そこには何十年もの間、止まることなく日本の社会インフラを支え続けてきた鉄壁の合理性と、コンパイラとハードウェアが極限まで最適化された美学が存在する。

今回は、その中でも基幹系バッチの生命線である VSAM(Virtual Storage Access Method) と、それを制御する PL/I の `ENVIRONMENT` 属性 について、コンパイラの裏側の挙動からマイグレーションの罠に至るまで、徹底的に解剖していこう。

PL/Iが他の言語(COBOLなど)と決定的に違うのは、言語仕様の中にハードウェアやOSのアクセス方式への直接的なフックが洗練された形で組み込まれている点だ。そして、その制御の要となるのが `DECLARE … FILE … ENVIRONMENT` である。

—

1. VSAMデータセットの種類と PL/I `ENVIRONMENT` 属性の極意

VSAMには、KSDS(Key-Sequenced Data Set)、ESDS(Entry-Sequenced Data Set)、RRDS(Relative-Record Data Set)という主要な3つのデータセットタイプが存在する。それぞれの特性を無視したコーディングは、夜間バッチのパフォーマンスを致命的に劣化させるか、あるいは一発のアベンド(ABEND)を引き起こす。

それぞれの構造と、PL/I側でどのように定義すべきかを見ていこう。

KSDS (Key-Sequenced Data Set) — 主記憶と直結する木構造の支配者

基幹システムのマスターファイル(顧客マスター、口座マスターなど)の9割はKSDSで作られている。B-Tree構造(CIとCAの概念)を持ち、キーを指定したランダムアクセス(直示検索)と、キー順の順次アクセスの両方が可能だ。

PL/IでKSDSを扱う際の設定の肝は、`ENV(VSAM ORGANIZATION(KEYED))` の指定と、適切なバッファリング(`BUFND`/`BUFNI`)にある。

1
/ KSDSマスターファイルアクセス用のPL/I定義例 /
DCL CUST-MASTER FILE RECORD
ENV(VSAM
ORGANIZATION(KEYED)
REUSE
BUFND(10) / データバッファ数 /
BUFNI(5) / インデックスバッファ数 /
);

DCL 1 CUST-REC,
5 Cust-Id CHAR(8), / VSAMキーフィールド /
5 Cust-Name CHAR(40),
5 Cust-Data CHAR(152);

/ 読み込み処理の抜粋 /
OPEN FILE(CUST-MASTER) INPUT;
READ FILE(CUST-MASTER) INTO(CUST-REC) KEY(Search-Key);
IF ^CUST_FOUND THEN DO;
/ レコード未存在時の例外処理 /
END;
CLOSE FILE(CUST-MASTER);

アーキテクトの現場知見:
KSDSの性能チューニングにおいて、`BUFND`(データバッファ)と `BUFNI`(インデックスバッファ)のデフォルト値(通常はそれぞれ2)のまま放置している現場が後を絶たない。オンライン(CICS)や大規模バッチでは、ここを明示的に引き上げるだけで、I/O待機時間が劇的に削減される。コンパイラオプションの最適化と合わせ、VSAMのチューニングはメインフレームSEの腕の見せ所だ。

ESDS (Entry-Sequenced Data Set) — ログと履歴のマグマ溜まり

ESDSは、レコードが書き込まれた順序(物理的な到着順)に格納されるファイルだ。後からキーの挿入や削除(論理削除を除く)はできない。トランザクションログやシステムジャーナル、監査証跡の保存に最適である。

1
/ ESDSログファイルアクセス用のPL/I定義例 /
DCL SYS-LOG-FILE FILE RECORD
ENV(VSAM
ORGANIZATION(SEQUENTIAL)
);

DCL 1 LOG-REC,
5 Log-Timestamp CHAR(26),
5 Log-Msg-Text CHAR(200);

/ 逐次書き込み処理 /
OPEN FILE(SYS-LOG-FILE) OUTPUT;
WRITE FILE(SYS-LOG-FILE) FROM(LOG-REC);
CLOSE FILE(SYS-LOG-FILE);

RRDS (Relative-Record Data Set) — 相対スロットの高速直撃

RRDSは、1から始まる相対レコード番号(SLOT番号)によって直接レコードを特定する構造体だ。ハッシュ済みのワークファイルや、固定長のステータス管理テーブルなどで真価を発揮する。KSDSのようなインデックス検索のオーバーヘッドがないため、理論上、最も高速なランダムアクセスが可能。

1
/ RRDSスロット管理ファイルアクセス用のPL/I定義例 /
DCL SLOT-FILE FILE RECORD
ENV(VSAM
ORGANIZATION(RELATIVE)
);

DCL Slot-Number FIXED BIN(31);
DCL 1 SLOT-REC,
5 Status-Flag CHAR(1),
5 Payload CHAR(255);

/ 相対番号を指定した読込 /
READ FILE(SLOT-FILE) INTO(SLOT-REC) KEY(Slot-Number);

—

2. ベース変数とポインタによる動的メモリ操作の危険な美学

PL/Iの真骨頂は、C言語の `pointer` 概念をさらに洗練させた(あるいは難解にした)ベース変数(Based Variable)にある。VSAMから読み込んだ可変長レコード(VB形式やVSAMのVSAM Variable)を効率よく処理するためには、ポインタと `BASED` 属性のコンビネーションが不可欠だ。

しかし、ここにはマイグレーション時に地獄を見る罠が潜んでいる。

1
/ ベース変数を用いた動的ストレージ操作の例 /
DCL Ptr-Buffer POINTER;

/ レコードレイアウトの定義(ストレージを占有しない) /
DCL 1 Var-Record BASED(Ptr-Buffer),
5 Rec-Length FIXED BIN(15),
5 Rec-Body CHAR(1000);

/ 領域の動的取得と解放(PL/IのALLOCATE文) /
ALLOCATE Var-Record;
/ または LOCATE 構文によるVSAM出力制御 /

マイグレーション(Java/C#化)におけるエッジケース

JavaやC#などのモダン言語へシステムを移行する際、この「任意のメモリアドレスを直接指し示すポインタ操作」や「ストレージオーバーレイ」の再現に苦しむことになる。
特に、PL/Iの `UNSPEC` 組み込み関数や、異なるデータ構造を同じメモリ領域にマップする技法(COBOLの `REDEFINES` に相当、PL/Iではベース変数や `CONTROLLED` 変数で実現)は、型安全性を重視するモダン言語の設計思想と真っ向から対立する。

移行設計では、単なる構文変換ではなく、データ構造全体のシリアライゼーション/デシリアライゼーション層の再設計が必須となる。

—

3. アベンド(ABEND)発生時のダンプ解析とエッジケース対策

夜間バッチが `S0C4`(Protection Exception)や `S0C7`(Data Exception)でアベンドした瞬間、ベテランSEの血が騒ぐ。PL/Iアプリケーションのダンプ解析において、VSAMとメモリ管理の観点から知っておくべきポイントを伝授しよう。

S0C7 アベンドとパックデシマルの内部符号反転バグ

PL/Iで `FIXED DECIMAL`(パック10進数:`PIC ‘9(n)V9(m)S’` など)として定義された領域に、空白や不正な文字データが混入した状態で算術演算を行うと、硬件が `S0C7` を発生させる。
特にVSAMファイルから読み込んだデータが、外部からの不正なファイル転送や、JCLのDD誤りによって文字化けを起こしているケースで頻発する。

対策:
PL/Iのコンパイラオプションで `CHECK(STG, OVERFLOW)` などを有効にすることが開発段階では推奨されるが、本番稼働時はパフォーマンスの観点からオフにされることが多い。そのため、ファイル入力直後に `VALID(Numeric-Field)` 組み込み関数を用いてデータの正当性検証(バリデーション)を行う防衛的プログラミングが、基幹システムを守る最後の砦となる。

VSAMファイルステータスのハンドリング

COBOLの `FILE STATUS` に相当する機能として、PL/Iでは `ON` 条件(Condition)を用いた例外処理機構が用意されている。

1
/ I/Oエラーのトラップ /
ON UNDEFINEDFILE(CUST-MASTER)
GOTO FILE_OPEN_ERR;

ON KEY(CUST-MASTER)
BEGIN;
/ VSAMキー重複やレコード不存在のエラーコード取得 /
DCL Err-Code FIXED BIN(31);
Err-Code = ONCODE();
DISPLAY(‘VSAM ERROR CODE: ‘ || TRIM(CHAR(Err-Code)));
GOTO VSAM_ERR_PROC;
END;

`ONCODE()` が返す数値を正確に把握しておくことが、迅速な障害切り分けの鍵となる。例えば、VSAM特有の論理エラー(例: 戻り値が特定の値を示すケース)を見逃すと、データ不整合を起こしたままバッチが正常終了(RC=0)するという最悪のシナリオ(サイレント・コラプション)を招く。

—

4. 埋め込みSQL(DB2)および CICS オンラインとの統合環境

今日のメインフレームにおいて、VSAM単体で完結するシステムは減り、DB2(リレーショナルデータベース)とのハイブリッド、あるいはCICS(Customer Information Control System)上のオンライン処理との共存が当たり前となっている。

CICS環境下における VSAM アクセス

CICSオンラインでは、VSAM(特にKSDS)は FCT(File Control Table) を介して高速にアクセスされる。PL/IプログラムからCICSコマンドを叩く際、ファイルの排他制御(ENQ/DEQ)やセッション管理のライフサイクルを意識しなければならない。

1
/ CICS環境下でのVSAM読み込み(EXEC CICS READ) /
EXEC CICS READ
DATASET(‘CUSTMST’)
RIDFLD(Cust-Id)
INTO(CUST-REC)
LENGTH(Rec-Len)
RESP(Cics-Resp);

IF Cics-Resp = DFHRESP(NORMAL) THEN DO;
/ 正常処理 /
END;
ELSE IF Cics-Resp = DFHRESP(NOTFND) THEN DO;
/ レコードなし /
END;
ELSE DO;
/ 異常系処理 /
END;

オンラインとバッチで同一のVSAMファイルを共有する場合、バッチ側の排他モード(`SHR(OLD,ALL)` vs `SHR(UPD,UPD)`)の設計を誤ると、オンラインの応答速度が低下するか、最悪の場合はファイル競合によるデッドロック(ABEND `AICA` や `AKEA` など)を引き起こす。システムアーキテクトは、JCLのDISPパラメータやVSAM定義(IDCAMSのDEFINE CLUSTER)におけるSHAREOPTIONSの指定まで目を光らせる必要がある。

—

5. レガシー移行(マイグレーション)を見据えたアーキテクチャの心得

最後に、今まさにPL/IからJava/C#、あるいはクラウド環境へのリライトや自動変換(トランスレーション)に挑むエンジニアへ、アーキテクトとしての警告とエールを送りたい。

1. 暗黙の前提に囚われるな:
PL/Iのデータアライメント(境界調整、`ALIGNED` / `UNALIGNED`)の挙動は、コンパイラのバージョンやアーキテクチャ(360/370系からz/Architectureまで)に深く依存している。バイナリ互換性を維持したまま移行する場合、このメモリレイアウトの差異が原因でバグの温床となる。
2. I/O層の抽象化:
VSAMの持つ高度なキー順・相対順アクセスやトランザクション整合性を、リレーショナルデータベースやNoSQLにマッピングする際、単なるSQL文への置き換えではパフォーマンスが破綻する。バルク処理(Bulk Insert/Fetch)の導入や、適切なインデックス設計の再構築が不可欠である。

メインフレームのコードは難解で古臭く見えるかもしれない。しかし、そこには「絶対に止まらないシステム」を構築するための先人たちの知恵と執念が詰まっている。その本質を理解した上で、次世代へのトランスフォーメーションを成功させてほしい。

健闘を祈る。

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