致命的 #753
完了WeakStorageのキーが削除されない
0%
説明
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中でのロックを外して様子を見てみます。
バックトレース
ɛ ʘɞʘ ɜ
toshi_a 初音 さんがほぼ10年前に更新
Ruby1.8の時にWeakStorage周りに不具合があって、ここで間違った値が返ってくると問題の特定が困難なのでそのへんでチェックしていたんだと思います。
Ruby2.1の仕様については全く知りませんでした。ただ、数週間とか起動しているんだけど、その問題に引っかかったことはないですね。とはいえ問題が起こる環境はどうやらあるらしいので、そのまま進めてください。
Osamu Koga さんがほぼ10年前に更新
delete_ifの最中にWeakStorage#[]=が呼ばれ、やっぱりエラーになる事案が発生しました。やはり割り込まれないなんてうまい話はなかった……。
ファイナライザ内で新しくスレッドを作ればロック掛けられるのかな?
toshi_a 初音 さんが9年以上前に更新
- ステータス を 新規 から 実装待ち に変更
- 担当者 を Osamu Koga にセット
こちらの認識だとエラーが発生する確率は減ってそうなんですが、実際どうなんでしょう。今日からリハビリ開始なので検証はできてません。
クラッシュする頻度が上がっていないのであれば、一旦この段階で出してしまっていいと思います
finalizerのコンテキストだとオブジェクト自身への参照はないので、SerialThreadとかに投げてやったうえでロックをかけるとうまくいくかもしれませんね。前にも書いたとおりこちらでは一度も再現できていないので、そちらで試してもらえますか。
toshi_a 初音 さんが6年以上前に更新
- ステータス を 実装待ち から 終了 に変更
- バックトレース を更新 (差分)
数年間、このようなクラッシュ報告はなかったためcloseします。
同様の問題が発生したら新たにチケットを作ってください。