[Wicket-ja-user 445] Re: After 1 minute the Pagemap null is still locked by  エラーについて

Zurück zum Archiv-Index

Tsutomu Yano t_yano****@me*****
2010年 5月 14日 (金) 01:57:29 JST


矢野です。

そのエラーは,おっしゃるとおり,Pagemapのロックが1分以上継続したことにより発生するものです。

> 同一セッションを利用してリクエストを投げた場合に、先行のリクエストがPagemapをロックしてしまい、先行のリクエストが処理に1分以上かかってしまうと、
> Wicket側で後続のリクエストを破棄し、表記のエラーをthrowしているところまではわかっているのですが。

まさしくこれが回答だと思います。


このエラーはPagemapというリソースの更新が競合するのを抑止するのがひとつの目的ですが,別の目的として,重い処理の連続起動を抑止する目的もあります。

Wicketはページマップ単位でユーザのリクエストを処理します(ページマップは,おおざっぱにいうとセッションをもう少し細分化したもので,もともとはブラウザウインドウ単位に用意される物でした。現在は少し違いますが)。ページマップ毎にリクエスト処理は直列に処理されます。前のリクエストがまだ処理中であれば,次のリクエストは一定時間,前の処理の完了を待ちます。この「一定時間」がデフォルトでは1分で,1分たっても前の処理が終わらなければ,リクエストをアボートして例外をスローします。

これによって,重い処理が行われるボタンをダブルクリックした場合に,重い処理が二回三回と連続起動することを抑止しています。

重い処理の二重クリックへの対策としては

1. 前の処理を落として新しいリクエストを処理する
2. 前の処理の完了を待つ
3. 前の処理は放置して実行させておき,新しいリクエストの処理を開始して,ブラウザには新しい処理の結果を返す

というくらいの選択がありますが,1 はサーブレットコンテナのスレッドを無理矢理終了する必要があり,3 は,重い処理が複数個並列実行されるうえに,並列実行されることにより正しい結果が返るかどうか不安定になるという危険があります。結果として,Wicketには2の制御が組み込まれているのです。

いつまでも待つわけには行かないので,タイムアウト時間を設けて,その間だけ待つことになっています。タイムアウト時間は,IRequestCycleSettings#setTimeout(Duration)によって変更することが出来ます。デフォルトは1分です。


結局のところ

> 表記の件につきまして、どういった場合にこのようなエラーが発生するのか現状良くわかっておりません。


1分以上かかる重い処理を起動するURLに,同じページマップで,1分以内に,複数回アクセスすることで発生します。一番あり得るのは,あるボタンを押すと1分以上かかる重い処理が起動するケースで,そのボタンをダブルクリックなど繰り返しクリックした場合,です。



このエラーが出た時の対策は

1. リクエストの処理に1分以上かかるのは重すぎると考えて,リクエストは処理の受付だけにとどめて,実際の処理はバックグラウンド化して,ユーザには別の画面で結果を確認出来るようにする
2. ユーザが1分でも5分でも待つのなら,敢えてタイムアウト時間を延ばす

のいずれかになるかと思います。ただ,2の場合は,処理は完了するものの,ユーザのブラウザには結果を返却できない可能性が高いです。ブラウザはすでに新しいリクエストを投げているので,その新しいリクエストのレスポンスを待っているからです。

現状のWicketのコードを見る限り,前述の3択対策の1つ目は組み込まれていません。


# SessionクラスのgetPage()メソッド内でこの処理が行われています。


エラーが発生したときに「ダブルクリックしましたね? やめてね?」という画面を表示する必要もあるでしょう。これは,WebApplicationクラスのnewRequestCycleProcessor()メソッドをオーバーライドし,独自のWebRequestCycleProcessorオブジェクトを返すことで実現できます。WebRequestCycleProcessorのonRuntimeException()メソッドをオーバーライドすると,このロック例外を捕捉できます。onRuntimeException()でPageオブジェクトを返却すると,Wicketはそのページに遷移しますので,独自のエラー画面を返すことで適切なメッセージを表示できます。
  onRuntimeExceptionメソッドにはRuntimeExceptionオブジェクトが渡されますので,getCause()メソッドで原因例外を再帰的にたどっていき,原因がWicketRuntimeExceptionでなおかつメッセージが「is still locked by」を含んでいれば,このロック例外と見なすことが出来ます。



補足として,ページマップについて簡単に解説しておくと,今のWicketのデフォルト設定では,セッション=ページマップです。複数のウインドウが同一のセッションIDで接続している場合,すべて同じページマップになります。

IPageSettings#setAutomaticMultiwindowSupport(true) を呼び出すことで,同一セッションであっても,ブラウザウインドウ毎にページマップが分かれるようになります。この状態ですと,あるウインドウで起動した重い処理は,別のウインドウで起動した重い処理をブロックしません。今回のエラーが発生した場合でも,別のブラウザウインドウを開いて起動すれば,エラーが発生せずに処理が起動します。

代わりに,セッションに格納されるオブジェクト数が増加することになりますし,重い処理が複数個起動することになります。


業務アプリケーションでは割と5分かかる処理をボタンで起動してのんびり待ってる,なんてこともありますが,ウェブアプリケーションとしては,リクエストに対する返事はせめて秒単位で返ってくるべきでしょうから,この機能とデフォルト1分のタイムアウトというのは,割と合理的なのかな,と思います。


という感じです。お役に立てばいいのですが。





On 2010/05/13, at 14:39, くり しゅう wrote:

> 栗本です。
> 初めて投稿させてもらいます。
> 
> 表記の件につきまして、どういった場合にこのようなエラーが発生するのか現状良くわかっておりません。
>  
> 同一セッションを利用してリクエストを投げた場合に、先行のリクエストがPagemapをロックしてしまい、先行のリクエストが処理に1分以上かかってしまうと、
> Wicket側で後続のリクエストを破棄し、表記のエラーをthrowしているところまではわかっているのですが。
> 
> 【発生時のログ】
> After 1 minute the Pagemap null is still locked by: 
> Thread[http-11080-Processor12,5,main], giving up trying to get the page for path: 156:logout
>  
> わかる方がいらっしゃいましたら、回答をよろしくお願いします。
>  
>  
>  
>  
> 
> 2010 FIFA World Cup News [Yahoo!Sports/sportsnavi]
> _______________________________________________
> Wicket-ja-user mailing list
> Wicke****@lists*****
> http://lists.sourceforge.jp/mailman/listinfo/wicket-ja-user




Wicket-ja-user メーリングリストの案内
Zurück zum Archiv-Index