本当にあったやらかしDB設計⑦【ステートフルDB】

どうも、最近満員電車に殺意を抱くようになった人です

今日は[本当にあったやらかしDB設計⑥【見えない削除フラグ】](https://qiita.com/bouitengineer12/items/d82e5507814d3f914524)に続いてびっくりしたことを紹介します

#ステートフルDB

そもそもステートフルDBとは何でしょうか
ステートフルDBとは、**状態遷移をDBが管理**していることです
DBの値を0や1にすることによってアプリケーションの挙動を変えようとしてしまっています
更に、更新頻度が異常に高いです

削除フラグについては[前回](https://qiita.com/bouitengineer12/items/d82e5507814d3f914524)も触れていますが、削除フラグに留まらず、○○フラグというものは手法としてはかなり悪手です

下記で説明されているので、是非見てみてください
[PostgreSQLアンチパターン](https://www.slideshare.net/SoudaiSone/postgre-sql-54919575)
DBに状態を持たせるのは危ない、事実のみを保存するようにする、と説明されています

ここでは更に掘り下げていこうと思います

###何が悪いの？？

DBは状態遷移をリアルタイムで監視できません
本来であれば、アプリケーションで起こった事実を保存することしかできないです
何故かというと、アプリケーションとDBは別物だからです

アプリケーション側でインスタンスの状態を変えるために先にDBの値を予め変えておき、読み取る、というやり方はアプリケーションの設計の観点から言ってもおかしいです
メモリーの上に置いてあるインスタンスの状態を保存するためにDBを使って永続化(ディスク領域に保存)するのがアプリケーションがDBを使う本来の目的です

勿論例外もありますが、これについては後述します

他にも、〇〇フラグというものは0や1で表現されることがあり、データを見ても意味がわからないことがあります

###問題

ステートフルDBはいくつかの問題を引き起こします

 - **あるレコードの値を信用できなくなります**

ステートフルDBはレコードの値を頻繁に変更します
ステートフルDBを使っているアプリケーションは、○○フラグの値を確認し、0の場合の処理、1の場合の処理を行います
しかし、処理の開始時は0だったものが、処理中にDBへ変更が掛かり、処理終了時には0以外の値になっている可能性があります
DBの値が変わった瞬間に処理を止めようとすると、アプリケーションが処理中に何回も値を確認しなければならなくなったり、アプリケーションの処理が終了するまでロックを掛け続けることになります(やり方を間違えるとデッドロックの危険性もあります)

このやり方はパフォーマンスが無駄に劣化する上、あまり価値がありません

 - **ひとつ前の値がわからなくなる**

ステートフルDBは履歴のような使い方ができません
最新の状態はわかりますが、テーブルから過去の情報を読み取ることができなくなります
更に、状態が頻繁に変わるため現在の状態すらも既に変わっている可能性があります
最新の状態を確認、維持するためにより強いロックを獲得したり、頻繁に値を確認する必要が出てきてしまいます

<font color = "red">**本来保存したい過去の情報を、ステートフルDBは捨てます**</font color>

- **テーブルがフラグまみれになる**

0や1のような○○フラグは、それを見る人間に何の情報も伝えません
どのような意味があるのかは、アプリケーションのコードを読み解く必要性が出てきます

<font color = "red">**情報を伝えないDBは、情報を持っていないDBと同じです**</font color>

###原因
なぜこんなことが起こってしまうのか、考えてみました

- **DBとアプリケーションの見分けがついていません**

DBはデータを保存するための倉庫です
アプリケーションは倉庫にデータを入れたり、倉庫からデータを取り出して調理するのが仕事です
<font color = "red">つまり、同じデータを扱っていても、データに対する観点が違います</font color>

これを間違えると、アプリケーションとDBの関係性がおかしくなり、データの不整合を起こします

 - **アプリケーションでフラグを使いまくってる**

アプリケーションでも○○フラグは使うべきではありません
有名な[リーダブルコード](https://www.amazon.co.jp/%E3%83%AA%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89-%E2%80%95%E3%82%88%E3%82%8A%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E5%AE%9F%E8%B7%B5%E7%9A%84%E3%81%AA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-Theory-practice-Boswell/dp/4873115655/ref=sr_1_1?adgrpid=56076398369&dchild=1&gclid=Cj0KCQjwoub3BRC6ARIsABGhnybVnQCla8rpLzPBfvdPOEXTGgeEW5e_7Ozxx4gXE4zxp9hnwEgKheIaAiSuEALw_wcB&hvadid=338571512561&hvdev=c&hvlocphy=1009332&hvnetw=g&hvqmt=e&hvrand=7756143446659507003&hvtargid=kwd-334758528225&hydadcr=27298_11561624&jp-ad-ap=0&keywords=%E3%83%AA%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89&qid=1593420505&sr=8-1&tag=googhydr-22)という本で**制御フロー変数**として紹介されているものがあります
これは、本来不要な変数を中継することによって可読性を落としています

ステートフルDBとは少し違いますが、イメージとしては同じようなものです
このような○○フラグがアプリケーションで頻繁に用いられていると、本来は必要ないのに、DBに保存し、DBで状態遷移を管理しようとしてしまいます

###解決方法

#####必要のない○○フラグを消そう！
よく精査して、いらない○○フラグを消すようにアプリケーションをリファクタリングしてください
アプリケーションで扱う○○フラグが少なくなれば、DBに保存したい値も減るはずです
特に、更新頻度が高いデータに注意してください
わざわざDBに保存する必要がないデータの可能性があります

#####履歴を残そう！

updateをするのではなく、insertをしてください
これによって過去の情報を捨てずに済みます
または、別に履歴テーブル(ログテーブル)を作成してください

#####状態遷移はアプリケーションで制御しよう！
状態遷移はアプリケーションで制御し、DBにはその結果のみを保存してください
<font color = "red">**DBに保存されている結果を変更することによってアプリケーションの挙動を変えようとしないでください**</font color>
永続化するためにDBに保存しているのに、値を勝手に変えないでください
ただし、ニックネームや住所などの変更する可能性があり、アプリケーションの表示だけが変わる、というような場合はupdateしても良いです
それが本来の意味であるupdate(更新)だからです
このようにupdateを正しく使っていれば、更新頻度が異常に高くなることはありません

しかし、その場合でも履歴テーブル(ログテーブル)を用いて過去のデータを保存しておくべきです

#####値ではなく文字にしよう！
ステートフルDBには例外もあり、権限のようなデータを扱う場合は便利です
ただし、訳のわからない0や1などの値ではなく、"Administrator"や"user"のような意味のある文字にしてください
あ、勿論参照整合性は必要ですよ

###例外
ステートフルDBには他にも例外があります

サービス起動時の機能のON/OFFを切り替えたり、サービス起動時のデフォルト値を変えたりしたいときには便利です
コツとしては、サービス起動中に何回も値を確認しにいくのではなく、サービスの起動時など特定のタイミングで一回だけ読み取る、ということです
これであれば何回も値を確認する必要もなく、サービス起動中に変更が加わっても影響が出なくて済みます

#まとめ
 - 要らないものを作るな
 - 履歴を保存しろ
 - ○○フラグなんてクソみたいなもんは使うな

どうだったでしょうか
同じような仕組みでも少しやり方を変えて、ステートフルDBを使わないようにすることで得られるものがかなり有ります
より良いやり方を使ってください



- [本当にあったやらかしDB設計シリーズ一覧](https://qiita.com/bouitengineer12/items/e63e5f7d6f628da11362)