致命的 #753
closedWeakStorageのキーが削除されない
0%
Description
Ruby2.1.0以降でWeakStorageからキーが削除されなくなっており、以下のようなフロー(一例)で、プロフィール画面のツイートが表示されないなどの問題が発生しています。
- UserStream経由でユーザAのMessageが取得され、Home TLに流れてくる。
- Home TLの表示可能数がいっぱいになったため、MessageがHome TLから削除され、GCされる。
- 削除されたMessageと同じobject_idをもつ、別のオブジェクトが生成される(object_idはオブジェクトのアドレスとほぼ同じものなので、このような状況は頻繁に発生します)。
- ユーザAのプロフィールを表示しようとする。
- ユーザAの最新ツイートを元に、Message.new_ifnecessaryによってMessageが取得される。
- (キーが削除されていないので)WeakStorage#[]で対応するオブジェクトを取得しようとする。
- 実際には3.で違うオブジェクトが入っているので、type_strictに引っかかる。
- 例外が投げられ、ユーザTLは取得できなかったことになる。
Ruby2.1.0からファイナライザがシグナル割り込みと同じコンテキストで実行されるようになったため、finalizerの中でMonitorを使ったロックができなくなった(ロックしようとすると例外が投げられる・finalizer内の例外は握りつぶされる)ことが原因だと思われます。
参考: https://gist.github.com/osak/71648a7360d9a0a34f57
Rubyの実装を追いきれてないですが、Hash#delete_ifはI/Oじゃないので、途中でWeakStorage#[]とかに割り込まれて変なことになる恐れはない気がするので、とりあえずfinalizer中でのロックを外して様子を見てみます。
バックトレース
ɛ ʘɞʘ ɜ
Updated by toshi_a 初音 almost 10 years ago
Ruby1.8の時にWeakStorage周りに不具合があって、ここで間違った値が返ってくると問題の特定が困難なのでそのへんでチェックしていたんだと思います。
Ruby2.1の仕様については全く知りませんでした。ただ、数週間とか起動しているんだけど、その問題に引っかかったことはないですね。とはいえ問題が起こる環境はどうやらあるらしいので、そのまま進めてください。
Updated by Osamu Koga almost 10 years ago
delete_ifの最中にWeakStorage#[]=が呼ばれ、やっぱりエラーになる事案が発生しました。やはり割り込まれないなんてうまい話はなかった……。
ファイナライザ内で新しくスレッドを作ればロック掛けられるのかな?
Updated by toshi_a 初音 over 9 years ago
- Status changed from 新規 to 実装待ち
- Assignee set to Osamu Koga
こちらの認識だとエラーが発生する確率は減ってそうなんですが、実際どうなんでしょう。今日からリハビリ開始なので検証はできてません。
クラッシュする頻度が上がっていないのであれば、一旦この段階で出してしまっていいと思います
finalizerのコンテキストだとオブジェクト自身への参照はないので、SerialThreadとかに投げてやったうえでロックをかけるとうまくいくかもしれませんね。前にも書いたとおりこちらでは一度も再現できていないので、そちらで試してもらえますか。
Updated by toshi_a 初音 over 6 years ago
- Status changed from 実装待ち to 終了
- バックトレース updated (diff)
数年間、このようなクラッシュ報告はなかったためcloseします。
同様の問題が発生したら新たにチケットを作ってください。