背景
JSP, Servletを使用して
WEBサービスを開発しています。
あるFormDataを送ったらDBに登録する処理を行っており
それをstatic変数でsynchronized句で囲っています
その登録処理で主キーの最大値にインクリメントして
登録してるので複数動くと主キーが被りエラーになります。
目的
複数から同時にリクエストが来たときエラーが出ずに登録出来るか確認する
期待値はすべて登録させること
試したこと(検討中含む)
- サーブレットのインスタンスを生成し
登録メソッドを呼ぶプログラムをマルチプロセスで動かす
問題:(synchronizedはシングルプロセス上で複数スレッドからの処理の同期を取るためテストにならず) - GETリクエストで登録先にリクエストを送るURLを同一Tomcatに配置しそこにマルチプロセスでcurlする
問題 : クライアント -> 登録テスト -> 登録先の順でアクセスし登録テストに同時にたどり着いてもその先でスレッドに分割され優先順位がつけられるので厳密に同時ではない
質問
上記以外に方法ありますでしょうか?
> 上記以外に方法ありますでしょうか?
ご提示の方法では「どのような問題」があるために、別な方法を求めている感じなのでしょうか。
タイトル、マルチスレッドでなくマルチプロセスのほうが良いのでは。
問題点の追加をしました
検討案1はテストにならなかったのと
検討案2は同時ではなくなるのでは?とお?思っています
質問自体はマルチスレッド?プロセス?下でのテスト方法に関することでしょうが、そもそもの前提が妙なかんじがしています。
> あるFormDataを送ったらDBに登録する処理を行っており
> それをstatic変数でsynchronized句で囲っています
> その登録処理で主キーの最大値にインクリメントして
> 登録してるので複数動くと主キーが被りエラーになります。
JavaサーブレットでのWEBアプリは、単一のプロセスでリクエスト毎にスレッド化(マルチスレッド)、とのようなかたちが一般的だと認識していますが、synchronizedでアプリ中で適切に排他されているのであれば、競合することはなく、主キーが被るようになることは無いはずと思うのですが違うのでしょうか。
WEBアプリで主キーとなる値を新規に生成している、と言うことなのですよね。この方法自体が少し単純すぎる気がしています。
(単なるテストとか実験的なプロジェクトならありかもしれませんが)
DBへのアクセスを挟んでのsynchronizedでの排他は、I/Oを挟んで比較的長時間となるので、もともとの使い方が推奨されたかたちではないような気もします。
RDBMSの製品にもよるでしょうが、SELECT FOR UPDATEのようにDB側で排他をかけるのが一般的な方法のひとつではないでしょうか。
あるいは、生成した主キーが被ったら再生成してリトライするとか。
極端な話、複数のサイトでのホスティングで同じDBに更新かけたら、主キーが被ることがあるのは避けられません。
Javaサーブレットではなく他の同じような形態のWEBアプリや、最近の主流ではどうやっているのか興味がありますね。
複数クライアントから同時リクエストが到達した場合の対応と思われます。
一般にはデータベースのレベルで一意に採番すれば解決する。
Webレベルの直列化にはSynchronizer Tokenパターンが参考になりますが ... 。
> synchronizedでアプリ中で適切に排他されて
>いるのであれば、競合することはなく、
>主キーが被るようになることは無いはずと
>思うのですが違うのでしょうか
私も同じ認識ですが本当にそうだよね?を確認するためのテストです
私が新規で作ったわけではなく最小限の改修である必要があるため簡単にやり方を変更できないです。。
>主キーが被るようになることは無いはずと
>思うのですが違うのでしょうか
主キーをどこで採番していますか。採番SQLが読み/書き2つに分割されていますか。
> >思うのですが違うのでしょうか
> 私も同じ認識ですが本当にそうだよね?を確認するためのテストです
なるほど、そうだったのですね。そうであれば納得しました。質問の説明文中、以下の部分
> それをstatic変数でsynchronized句で囲っています
> その登録処理で主キーの最大値にインクリメントして
> 登録してるので複数動くと主キーが被りエラーになります。
を読んで、今現在エラーが発生することが実際にあったのかなと思いました。
わかりました。
tomocatのインスタンス(プロセス)が一つ(クラスタリングしない)という条件で、
synchronized句でstatic変数はキャッシュからメモリへの書き込みが保証されるので、他のスレッドから更新後の値が見えます。
syncronizedしていなければインクリメントした値がキャッシュに留まり他のスレッドから見えないことが起こりえますが、常に起こるかはわかりません。
マルチコアCPUを使用しているという前提
synchronizedスコープとDBトランザクションのスコープが一致しているか?
インクリメントが迂回されていないか?
登録SQLで同時に主キーをインクリメントすべきですね。