モデリング以外のRDB論理設計 ～ ステータス管理編 ～

#### 障害に強いシステムに不可欠なステータス管理

　RDBの論理設計で<b><font color="MediumVioletRed">モデリング以外で重視すべきポイント</font></b>の2回目は<b><font color="MediumVioletRed">ステータス管理</font></b>をテーマにする。<br><br>
　障害に強いシステムを構築しなくてはならない。このような要件が与えられた場合、アプリケーションはどのように構築すべきだろうか。運用ミスを防ぐ機能（人間が入力する情報の入力チェックの厳格化等）や、異常を検知する機能の実装は当然だが、万一、運用ミスが起きても、その悪影響を最小限に抑え、速やかに正常な状態に復帰できるよう構築すべきだ。その際、重要になるのがステータス管理である。<br>
***
 ステータス管理の考え方を、あるシステムで次のように処理が行われたと仮定し、以下に記す。<br>

|順序|内容|
|:---:|:---|
|1|バッチで3件のレコード（A、B、Cさん用レコード）が請求テーブルに登録される。|
|2|フロントでBさん用レコードを削除する。|
|3|フロントでCさん用レコードを訂正する。<br>（「請求金額」を10万円から8万円に訂正する。）|
|4|Dさん用レコードが登録されていないことが判明する。<br>フロントでDさん用レコードを登録する。（「請求金額」は3万円。）|
|5|その後の調査でバッチ用入力データにD、Eさん用のデータ漏れが判明する。<br>それらを追加後、バッチを再起動する。<br>なおDさん用の本来の請求金額は5万円であった。|

- ここでいうフロントとは「サービスの利用者」や「サービス提供者側の運用に携わる人間」がブラウザや専用アプリを用いてシステムを操作することを意味する。
- このシステムの前提として、バッチで請求テーブル登録完了後に、元となった情報は削除されることもなくバッチ処理前の状態で残っているものとする。（請求の元となったレコードに「バッチ処理済み」という類のフラグも持たないものとする。）
- この前提に違和感を覚える人がいるかも知れないが、現実のシステムでは「バッチが登録するデータは、複数のテーブルから複雑なロジックで決定される」「バッチが参照する情報はCSVファイル等の可能性もある」等も珍しくないため、これを前提とした。

***
上記 5 のバッチ再起動後の請求テーブルは、次のようになることが望ましい。

|対象者|内容|
|:---:|:---|
|Aさん|1件のみ存在すること。|
|Bさん|存在しないこと。|
|Cさん|1件のみ存在すること。特に「請求金額」は8万円であること。|
|Dさん|1件のみ存在すること。特に「請求金額」は3万円であること。|
|Eさん|1件のみ存在すること。|
 
<br> 
***
　これを実現するためには、次の2点の対応が必要になる。
 
- 請求テーブルにステータス情報を管理するカラムを設ける。
- バッチはステータス情報を元に請求テーブルにレコードを登録する・しないを決定する。
 
　まず「ステータス情報を管理するカラム」だが、以下のように最低でも「ステータス」「最終更新源泉分類」「初期登録源泉分類」が識別できるようにする。

|カラム|データ型|設定タイミング|内容|
|:---:|:---:|:---|:---|
|ステータス|Number(1)|Insert・Update時|0:有効 1:無効（いわゆる論理削除）|
|初期登録源泉分類|Char(1)|Insert時のみ|B:バッチ F:フロント|
|最終更新源泉分類|Char(1)|Insert・Update時|B:バッチ F:フロント|

　次にバッチだが、「登録しようとする情報」を「ステータス情報を管理するカラム」に応じて処理を決定する。<br> 
- 「登録しようとする情報」が存在し、なおかつ「最終更新源泉分類」が B （バッチ）であれば、既存情報を削除後に再登録する。（この例はD、Eさんの漏れによる再起動だが、大元の情報を訂正する等も考えられるため、前回登録分を削除後に再登録する。）
- 「登録しようとする情報」が存在（論理削除状態も「存在する」とみなす）し、なおかつ「最終更新源泉分類」が F （フロント）であれば登録してはならない。
- 登録しなかった場合、警告ログ等を出力し「なぜ登録しなかったのか？」がわかるようにする。再起動時のバッチでは「Bさんのレコードはフロントで削除されていたため登録しなかった」「Cさんのレコードはフロントで訂正されていたため登録しなかった」「Dさんのレコードはフロントで登録されていたため登録しなかった」が理解できる警告ログ等を出力する。

　この方式は<b><font color="MediumVioletRed">「人間が何らかの事情に基づき登録・更新・削除する情報」は「バッチが一定の法則に基づき自動で登録・更新・削除する情報」よりも優先されるべき</font></b>という考え方が根本にある。
 <br><br> 
　 そのため、この例では<b><font color="MediumVioletRed">フロントでの処理結果をバッチよりも優先</font></b>している。（論理削除は不要という意見がある。削除フラグだけの単純なものは確かに不要だが「バッチで自動登録された情報が、何らかの事情のため、人間が削除した」ということを把握したいケースでは論理削除は必要となる。）<br><br>
　なお上記はあくまでもステータス管理の考え方を説明するための簡単な例である。バッチなら「自システム内でクローズしたバッチ」「他システムから転送されてきたデータを反映するバッチ」、フロントなら「通常利用者」「管理職権限の利用者」等のように<b><font color="MediumVioletRed">データが登録・変更・削除された原因（上記例の最終更新源泉分類）の優先順位</font></b>を勘案し、データの登録・変更・削除を許可する・しないを決定する。

***
#### ステータス管理が不十分なシステムのイメージ

　ステータス管理が不十分なシステムでは、上記のようなバッチ再起動を行った後は、次の2パターンいずれかになる可能性が高い。<br>

##### パターン1（削除後に再登録するパターン）
 
|対象者|内容|
|:---:|:---|
|Aさん|1件のみ存在する。|
|Bさん|1件のみ存在する。|
|Cさん|1件のみ存在する。但し「請求金額」は10万円である。|
|Dさん|1件のみ存在する。但し「請求金額」は5万円である。|
|Eさん|1件のみ存在する。|

##### パターン2（削除せずに再登録するパターン）
 
|対象者|内容|
|:---:|:---|
|Aさん|2件存在する。|
|Bさん|1件のみ存在する。|
|Cさん|2件存在する。「請求金額」は、1件は8万円、もう1件は10万円である。|
|Dさん|2件存在する。「請求金額」は、1件は3万円、もう1件は5万円である。|
|Eさん|1件のみ存在する。|

##### 各パターンの特徴

- パターン1（削除後に再登録するパターン）の場合、バッチ再起動後に「Bさんのレコードを削除」「Cさんの請求金額を8万円に訂正」「Dさんのの請求金額を3万円に訂正」という障害時運用が必要になる。
- パターン2（削除せずに再登録するパターン）の場合、バッチ再起動後に「Aさんのレコードを1件削除」「Bさんのレコードを削除」「Cさんの請求金額10万円のレコードを1件削除」「Dさんの請求金額5万円のレコードを1件削除」という障害時運用が必要になる。（みずほ銀行や三菱UFJニコス等は、二重引き落とし障害を過去に起こしたが、おそらくこのパターンの障害時運用に何らかの問題があったと思われる。）
- いずれのパターンも障害時運用の設計は業務系システムに精通した人間に頼らざるを得なくなりがちである。障害に強いシステム構築を目指すのであれば、属人性の低いステータス管理方式を検討すべきである。

<br>
以上最後まで目を通して頂き、ありがとうございました。