昨年春ごろから、SQL Server を使ってサーバーアプリを構築しています。
普段はクライアントサーバー方式で、サーバーはオラクルデータベースのみを設置するような方式をとるのですが、
この案件では SQL Server を使用し、サーバー側で処理をするプログラムとなるので、全然方式が違います。
まずは、サーバーのプログラムはサービスにする必要があります。
サービスとは、Windowsが起動すると同時に動作するように設定されたプログラムですが、メニューにあるスタートアップ
などと違い、デスクトップを表示しなくても動作する内部プログラムです。
もしサーバーが再起動したときでも画面にログオンしなくても動作するので便利です。
というより、サービスにしないとそもそも使い物になりません。
この場合、処理にスレッドは使用しません。
スレッドを使用するとよいように思えますが、クライアントからのアクセス数が確定できないものなので、いくらでもスレッドで
要求を受け付けるのは大変危険です。
ですので通常のイベントドリブンでマルチ的に動作する仕組みになります。
同時に同じレコードへのアクセスが発生した場合は、それが原因でデータベースの不整合が発生する可能性があります。
不整合の発生する原因としては
1.レコード番号などのユニークキーが重複する
2.同一レコードの更新処理がかぶる
ということがありますが、回避方法があります。
1は、SQL Serverであれば、IDENTITY というプロパティがありますので、これを使用してINSERTしたときにOUTPUTで追加した番号を取得する方法があります。
もしこれはオラクルであれば、SEQUENCEを使う方法とか、SELECT - FOR UPDATE などを使ってレコード情報を管理することで回避が可能です。
こういうオプションはSQL Serverにはなく、やはりオラクルは便利だと思いました。
2は、処理的に同じレコードのデッドロックがかかる処理が発生しないような設計にします。
特にサーバープログラムでは、そのプログラム1つだけでデッドロックがかかるので非常に厄介なので、絶対に発生しないような構造が必要です。
と、このように処理を作成していたのですが、どうも動きがあやしいところが出てきました。
そうして色々と解析しているうちに、ある重大なことに気がつきました。
DataReaderは、1つのコネクトに対して1つしか使用できないという制約があります。
ふつうのクライアントアプリでは問題ないのですが、マルチで動作するサーバー側アプリなどでは、他の処理でDataReaderで処理をしている最中に、別のdataReaderは動作できません。 (いったんクローズしないといけないのです!)
理由は簡単で、DataReaderは現在よみとっているレコードのポインタとそのレコード1行のデータのみメモリに読み込む方式だからです。
そのため、メモリを占有しないので大容量のデータを処理するときに用いられます。
ということで、DataReader記述をすべて DataAdapter/DataSet 記述に変更しました。
よく考えてみればこの処理では、データ結果がほぼ1件しかかえって来ない検索でしたので、この方式で全く問題がなかったのです。
サーバーアプリですと、デバッグログもなかなかチェックしにくいので、私はデバッグログ自体もデータベースに出力するようにしています。
こういうときにも、DataReaderを使用していると、オープンからクローズの間は別処理が行えませんので、ログも吐き出すことができなくなりますので、できる限り DataAdapter/DataSetに変更するのが望ましいといえますね。