AgileWorksからSAPへデータを連携する処理で、勘定科目コードをSAP側の原価要素コード(9桁)へ読み替える処理を追加しました。対応は10件程度だったので、読替表はシェルスクリプトの中に直接書いています。テストでは代表的なコードで正常に変換できていたのに、本番切替後の夜間連携が止まりました。原因は読替表に存在しないコードが2件混ざっていたこと。しかも1件は、登録済みのコードと1桁しか違いませんでした。この件を通して分かった「代表データのテストでは防げない理由」と、直打ちのまま事前に洗い出す方法を整理します。
夜間のSAP連携ジョブが、6秒で異常終了した
SAP向けのFTP連携ジョブネットは、毎日23時00分に起動します。異常検出終了になったのは、起動の6秒後でした。
ジョブネットの構成は、AgileWorks側から申請データをCSVへエクスポートし、複数のファイルを統合・加工して、最後にFTPでSAPへ送るという流れになっています。落ちたのは、統合と読替を行う前段のジョブでした。FTP転送までたどり着いていません。
止まった時点で送信済みのデータはゼロ。中途半端に送られていなかったのは不幸中の幸いでした。ただし、SAP側では当日分の連携データが一切届いていない状態になります。
AgileWorks側のコードは変えず、SAPへ送るときだけ読み替えていた
この連携では、AgileWorks側で使っている勘定科目コードと、SAP側で必要な原価要素コードの体系が違います。SAP側は9桁です。
既存システム側のコードをすべて9桁へ変更すると、影響範囲が一気に広がります。過去データ、画面、帳票、別の連携処理も、すべて従来のコードを前提にしているためです。
そこでAgileWorks側の体系は維持したまま、SAPへ連携する段階でだけ読み替える方式にしました。連携処理の中で勘定科目コードを受け取り、対応する原価要素コードへ変換してから出力します。
この方式には、新しい前提が1つ増えます。連携対象データに出てくる勘定科目コードが、すべて読替表に載っていることです。今回の障害は、この前提が崩れたことで起きました。
読替表は10件程度だったので、シェルスクリプトに直接書いていた
読み替えが必要な勘定科目は10件程度でした。この規模であれば、データベースにマスタテーブルを作るより、処理スクリプトの中に対応表を直接書いたほうが素直です。
テーブルを1つ増やせば、そのテーブル自体の権限設計、バックアップ対象への追加、他環境への反映手順が付いてきます。10件の固定的な対応表のために払うコストとしては重い、と判断しました。
この判断自体は、今でも間違っていなかったと思っています。ただし直打ちには、後で効いてくる性質が1つあります。中身を変えられるのがソースを触れる人間だけになる、ということです。
ログを見ると、1桁違いのコードが未登録だった
翌朝、ジョブの実行ログを確認しました。読替処理は1行ごとに結果を出力する作りにしてあったので、どのコードで落ちたかはすぐ分かりました。
[23:00:05] 正常: データ行が存在しません → export\data_a.csv
[23:00:06] エラー: 読替表に存在しない勘定科目_コードです → export\data_b.csv の 1 行目: 1200
[23:00:06] 正常: 勘定科目_コードを原価要素へ読替 → export\data_b.csv の 2 行目: 685020 → 600130000
[23:00:06] エラー: 読替表に存在しない勘定科目_コードです → export\data_c.csv の 1 行目: 635540
[23:00:06] 正常: 勘定科目_コードを原価要素へ読替 → export\data_c.csv の 2 行目: 685540 → 600620060
[23:00:06] 出力ファイル: export\(SAP向け出力ファイル)
[23:00:06] 統合されたレコード総数:2件
未登録だったのは「1200」と「635540」の2件です。
注目してほしいのは、その直後の行です。「685540」は正常に読み替えられています。未登録だった「635540」とは、2桁目が違うだけです。
読替表を目視で確認したとき、63で始まるコードと68で始まるコードが並んでいれば、似た番号を見て「これは入っている」と判断してしまいます。10件しかないからこそ、一覧を眺めて安心してしまった面がありました。
もう1件の「1200」は、他のコードが6桁なのに対して4桁です。桁数が違うコードが混ざることを想定できていませんでした。
そしてこのとき統合されたレコードは、合計で2件です。わずか2件のデータで、連携全体が止まりました。
データの不備なのに、プログラム修正になった
対応としては、不足していた2件を読替表へ追加し、ジョブを再実行して正常終了を確認しました。
ここで、直打ちにした影響が出ます。読替表はスクリプトの中にあるので、2件を足す作業はソースの修正です。ファイルを編集し、配置し直してから再実行する、という手順になりました。
これがマスタテーブルであれば、話は変わっていたはずです。運用担当者が管理画面やSQLから2行追加すれば済みますし、誰が追加したかの記録も残ります。
直打ちの場合、対応できるのはソースを触れる人間だけです。ひとり情シスの環境では、それは自分ひとりを意味します。今回は翌営業日の朝に対応できましたが、もし長期休暇と重なっていれば、データは滞留し続けていました。
ジョブの再実行にも注意が必要です。今回はFTP転送前に止まっていたため、SAPへの送信済みデータはゼロでした。転送後に落ちていれば、そのまま流し直すと二重送信になります。原因を取り除く作業と、滞留したデータを安全に処理する作業は、分けて考える必要があります。どこから再実行できるかは、ジョブネットの構成次第で変わります。
10件で直打ちにした判断自体は、間違っていなかったと思う
この障害のあと、読替表をマスタテーブルへ移すべきだったのか、を考えました。
結論としては、移す必要はなかったと思っています。10件の固定的な対応表のためにテーブルを増やすのは、やはり過剰です。仮にマスタ化していても、未登録のコードが本番データに混ざる、という問題そのものは同じように起きます。
変わるのは、気づきやすさと、直せる人の範囲です。
- マスタなら、SQLで元データと突合すれば未登録を事前に検出できる
- マスタなら、情シス以外でも追加できる
- 直打ちなら、突合には定義側を取り出す手間がかかる
- 直打ちなら、追加はソース修正になり、対応できる人が限られる
つまり本当の問題は、置き場所ではありませんでした。読替表をどこに置くにせよ、本番データに出てくるコードと突き合わせる手段を用意していなかったことが原因です。
勘定科目コードは、今後も増えます。一方で読替表は、作った時点の内容で固定されています。増え続ける側と、固定されている側を突き合わせる仕組みがなければ、同じことは必ず再発します。
事前に洗い出すには、データ側と定義側の両方を一覧にして突合する
マスタテーブルであれば、元データにLEFT JOINして未登録のコードだけを抽出できます。直打ちの場合は、定義側を一覧として取り出すところから始めます。
手順は3段階です。
# 1. 連携対象CSVから勘定科目コードを一意に抽出する(3列目にある場合)
cut -d, -f3 export/*.csv | tr -d '"' | sort -u > /tmp/codes_in_data.txt
# 2. 読替表側のキーを一覧にする
./convert.sh --dump-map | cut -d, -f1 | sort -u > /tmp/codes_in_map.txt
# 3. データにあって読替表にないコードだけを表示する
comm -23 /tmp/codes_in_data.txt /tmp/codes_in_map.txt
今回のデータであれば、この結果に「1200」と「635540」が並んでいたはずです。実行にかかる時間は数秒で、夜間ジョブが落ちてから翌朝まで気づかなかったことを考えると、割に合わない作業ではありません。
ポイントは手順2です。スクリプトに「読替表の定義を一覧出力するオプション」を持たせておくと、この突合が安定します。ソースをgrepで直接パースすると、書き方を変えるたびに抽出が壊れます。定義を1か所にまとめ、そこを出力するだけの引数を足しておけば、突合スクリプト側は何も直さずに済みます。
この確認は、本番切替前に一度やれば終わりではありません。連携対象のデータは毎日変わるので、ジョブネットの先頭に組み込み、未登録コードが見つかったら後続を止めるのが本来の形です。読替処理まで進んでから落ちるより、入口で止めたほうが原因が明確になります。
未登録コードを表示するだけでなく、そのコードが何件のデータで使われているかを集計しておくと、対応の優先度も判断できます。
23時に落ちて、気づいたのは翌朝9時だった
この件で、読替表とは別に残った課題があります。発見が遅れたことです。
ジョブが異常終了したのは23時00分06秒、それを確認したのは翌朝の9時過ぎでした。約10時間、誰も気づいていません。読替表の2件を直すこと自体は10分程度の作業でしたが、それが始まるまでに10時間かかっています。
原因を取り除く速さより、気づくまでの時間のほうが、実際の業務影響を決めていました。夜間バッチの障害では、ここが効いてきます。
ジョブが落ちたこと自体はJP1の画面上で赤く表示されていましたが、それを見にいくまで分かりません。異常終了時の通知経路を決めておく必要があります。夜間バッチが動かなかったことに気づく仕組みについては別記事で整理しましたが、今回のように「起動はしたが異常終了した」ケースも同じ考え方で拾えます。
まとめ:直打ちが悪いのではなく、突合しないことが問題だった
今回の障害を整理すると、こうなります。
- 読替表に未登録のコードが2件あり、夜間のSAP連携が6秒で異常終了した
- 未登録だった1件は、登録済みのコードと2桁目が違うだけだった
- 統合対象はわずか2件のデータだったが、連携全体が止まった
- 読替表がソース直打ちだったため、2件の追加がプログラム修正になった
- 発見まで約10時間かかり、修正作業そのものより長かった
10件程度の対応表をスクリプトに直接書くこと自体は、妥当な判断だと今も思っています。反省すべきは、本番データに出てくるコードを全件洗い出して、読替表と突き合わせる確認をしていなかったことです。
コード変換を挟む連携では、「変換できること」を確認するだけでは足りません。「現在存在するすべてのデータを変換できること」を、本番データそのもので確認する必要があります。代表データを使った正常系テストは、この確認の代わりにはなりません。
なお、この連携はJP1で夜間に自動実行しています。JP1/FTPを使った夜間バッチ連携の構成については別記事にまとめました。


コメント