見出し画像

排他制御って

機能仕様や業務仕様にはほとんど出てこないため、経験の浅いエンジニアにありがちなのが

「共有リソースへの排他処理を忘れる事故」

です。排他とは

一般に、特定のファイルやデータに対するアクセスや更新を制御すること。特にファイルやデータベースへの書き込み処理を行なう際に、データの整合性を保つためにアクセスやデータの読み書きを一時的に制限すること。

一人のユーザーがデータベースにアクセスしていることを前提にしていた90年代前半までは、「システム」というものはスタンドアロン形式であることが前提で、データベースアクセス制御そのものに関してはさほど難しく考える必要がありませんでした。

しかし、インターネット技術の飛躍的な向上に伴い、複数のユーザーが同時にデータベースにアクセスすることはよくあるどころか、むしろ受発注システムや座席予約システムなど、実用データベースアプリケーションの殆どは、複数のユーザーが同時に使うことを前提にしていると言っていい時代になってしまっています。

こうしたマルチユーザーが当たり前となった環境では、ユーザーが1人のときには無かったさまざまな問題が起こります。

ダーティリード

たとえば、USER1がトランザクションの途中で特定のレコードの内容を変更したあとで、別のUSER2がそのレコードを読み込んだとしましょう。そのあとでUSER1が値を変更したトランザクションをコミットすると、USER2は誤ったデータを読み込んだことになります。

画像1

あるトランザクションをしているのと別のユーザーが、そのトランザクションでまだコミットしていないデータを読み込んでしまうことを「ダーティ・リード」と呼びます。ダーティ・リードを回避するためには、コミットしていないデータを別のユーザが読み込めないようにしなければなりません。

実際、この排他が実装されていないシステムがリリースされ、海外の方相手に事故が起こり、国際問題になりかけたトラブルを解決したことがあります。

排他制御を含むトランザクション管理を、フレームワーク自体が代わりに行ってくれるケースもあります。そうなると、エンジニア自身はあまり排他制御を考慮する必要がなくなります。楽…ではあるのですが、そうやって「意識」の外になってしまうと、いざそのフレームワークを用いないシステム開発となった時に同じ事故を起こしてしまいかねませんので、便利になるというのは案外諸刃の剣であるということも知っておくといいでしょう。


ファントム

仮にコミットしていないデータを外部から読めないようにして、ダーティ・リードを防いでもまだ問題は起こり得ます。

先の例でUSER2が同じレコードを2度読み込むような場合、1度目と2度目の読み込みの間にUSER1がトランザクションをコミットすると、1度目に読み込んだ内容と2度目に読み込んだ内容が異なる可能性があります。

画像2

このように、複数回の読み込みの結果が、ほかのトランザクションのコミットのタイミングによって変わってくることを「反復不可能読み込み(non-repeatable read)」と呼びます。

加えて、2回の読み込みの間にUSER1のトランザクションがレコードを追加したり削除したら、2回目の読み込みでは1回目には無かったレコードが現れたり、それまであったレコードが無くなったりすることになります。この現象を「ファントム」と呼びます。

こうした問題に対応するために、データベースではトランザクション管理の"分離レベル"を設定できるようになっています。


解決方法

もっとも簡単な方法は、それぞれのユーザが一つひとつ順番に(シリアルに)処理が実行されるように実装することです。そうすれば、トランザクションが互いに影響を及ぼし合うことはなくなります。

しかし、それでは単位時間内に実行できるトランザクションの数が極端に少なくなり、パフォーマンスが低下してしまうため、マルチユーザー環境が至極当然となった現代においては致命的となってしまいます。

ここはエンジニアの『設計スキル』の見せ所です。

実装だけで何とかするエンジニアも中には存在しますが、それはチームメンバー全員ができるほど一般的な存在ではないので、メンバー依存で任せきりにしていると間違いなく大きな問題へと発展していくことでしょう。


また先ほども申し上げたように、昨今ではWebシステムを代表されるように、フレームワーク利用して簡便にトランザクション管理を行ってくれる機能も増えてきました。その結果、設計時点でトランザクション管理や排他機能に関する設計をしっかりと行うエンジニアは非常に少なくなってきているように感じられます。

ひょっとするとダーティリードやファントムと言った用語すら、知らない人も多いのではないでしょうか。

こうした観点は、ソフトウェア開発に精通していない顧客としては気づくことがありません。そのため、機能要求仕様には滅多に出てきませんが、明確に非機能で検討する内容ともされていないことが多いため、発注側も開発側もついつい見逃しがちになってしまいます。

私たち、エンジニア側がしっかりとフォローしないと、あっという間に不良を混入させてしまいますので、気を付けるようにしたいところです。



さいごに

ベテランエンジニアに言わせれば、おそらくは『常識』と言われるであろうこの排他制御ですが、あまりに常識と考えすぎているため新人や若手エンジニアにきちんと教育を施していない企業は意外に多いものです。

だから未実装で問題となる。

また、設計時点で考えられていないということはもちろんなのですが、もっとマズいのが「設計書」にも排他を明確にしたものが存在しないことが多いという点です。

せめて、ファイルやデータベースなど共有リソースに対する『CRUD図』が存在してくれてさえいれば、排他に関するテストを実施すべき箇所はすべて特定できるのですが、CRUD図を作成している開発現場というのは案外少ないものです(実装にはあまり関係ないと思われているため)。

さらにCRUD図は、インデックス設計時にも非常に役に立ちます。

個人的にはCRUD図を作成しないシステム開発って、本気で成功させる気あるのかな?という気がするんですけど、意外となんとかしているんですよね…現場って。そして、何とかなってない現場で問題が多発するのもCRUD図が無い現場のことが多いですね。


ちなみに。

昨今のシステムにおける非機能要件では、単純に性能面の評価だけでなく、次のようなことも機能要件には出てきにくい内容であるため、検討しておく必要があります。

図23

ソフトウェア開発に関わっていくのであれば、『要件に書いてあることだけが仕様ではない』ということは必ず肝に銘じておきたいですね。

いただいたサポートは、全額本noteへの執筆…記載活動、およびそのための情報収集活動に使わせていただきます。