YJIT有効化→なんか分かんないけど詰まりづらくなる→そして伝説へ……
にはまずバージョンアップがいろいろとあり大変そう
と雑に書きましたが、 @zundan さんはその後どんな感じで運用なさってますか?
@tadd 弊ぼっちはメモリ512MBにぎゅうぎゅうにプロセスを詰めてるのでJITは無しです。YJITだとメモリ2倍くらい必要になる感じなんですよね…。
ローカルから発言してるのは僕だけで、リモートからみた使い心地はリモートへの配達の遅延に依存してると思うんだけど、リモートへん配達の遅延はネットワークがボトルネックでだいたい1秒/送信先に決まっちゃってて、JITするよりメモリを節約してスレッドを増やす方がいい感じになっちゃってます。
@zundan どうもすんません、こちらの鯖のイキが悪い状態でした。
うわ〜、二倍。それは辛いですねー、クラウドでそこがお金のネックになるならなおさらつらい。
そうかしかし、ずんださんちだとネットが固定でネックだからJITはあんまり関係ない感じなのでしょうか。いろいろな鯖が居ますね。
昔のをざっと見た限りですが、YJITの特性的には、コードのどこをどのタイミングで、あるいは全体でどれくらいJITするかは結構調節しやすそうだったので、メモリを気にするJITとかもパラメータちょちょいでできないかなーと妄想していました。
@zundan と--helpを見ると、それなりに--yjit-*=というのがありそうではありました。ですがこの辺のチューニング、めんどくさいですよねぇ。特に結果(最大512MB、とか)じゃなくて過程の詳細(基本ブロックごとに最大64MB、とか)を指定するわけで、なかなか。
@tadd なるほど~。そのうち挑戦してみたいもんです。
あ、そういえばRubyを3.1から3.2に上げてメモリのふるまいが変わったようで、何時間かに1度なにもかも遅くなる現象が起きたのは、full GCを抑制するようにしきい値を上げて対応できました。Herokuなのでプロセスの寿命は1日ちょっとしかなく、その中で最適化できるのは悪くなさそうです。
@zundan なるほどなるほど、それは「3.2になったら何時間かに1度なにもかも遅くなる現象が起きた」のでしょうか?
もしそうだとするとバグに近い気がして、逆にしきい値を下げてどうにか再現したいな、とか思ったりしました。
あとはたしかに、プロセスはそんな長く走らないという前提で言語エンジン側が何かする余地がまだまだあるのかなーとか考えたりはしました。
(AWS Lambdaとかはもろにそうですが、独自にいろいろ(制約付きで)やってそうで、他人の芝生的に見えてたのでした)
@tadd GCの件はバグではないと思っています。
遅くなる現象はプロセスの起動からしばらくしてからしか起きず、応答時間以外にはScoutで監視してるRubyプロセスのみのメモリ利用量くらいしか同時に変化のあるメトリクスをみつけられませんでした。この現象の頻度はRUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTORとRUBY_GC_OLDMALLOC_LIMITをgc.cの2倍に設定して下り、RUBY_GC_OLDMALLOC_LIMITをさらに2倍(デフォルトの4倍)にして出なくなりました。なので、full GCで遅くなっていたと解釈しています。
弊ぼっちは恒常的にスワップを使っているのでGCによる速度の低下の影響が十分メモリのある環境と比べて非常に大きいと推測されるので、一般的に対処の必要な現象ではないと思っています。
Ruby 3.1と3.2の違いを把握できてるわけではないので、もし確認するとすれば、3.1でRUBY_GC_OLDMALLOC_LIMITを小さくして同じ現象が見られるか試してみたい気がします。
@tadd それより!RailsがRedisから読もうとしたオブジェクトがundefined method `fetch_value' for nil:NilClassを起こしちゃうのはたぶん3.2特有でなんとかしたいでつ!
https://github.com/mastodon/mastodon/issues/23644
@zundan ほぉぉぉぉ、なるほど。Marshal関係の本体の挙動がおかしそう、という話でしょうか?とくにもしこれが3.2.0からそうなのであれば、確かに(いかにも)有り得そうな印象です。
第一印象から素朴に考えると、Redisバインディングも疑ってみたくなりますが……と若干思い出しましたが、ずんださんが上げようとしてダメだったやつ、でしたっけ。たしかにだいぶ古い(ままな)ので、ここだけの変化はないか。
https://rubygems.org/gems/redis/versions
https://rubygems.org/gems/hiredis/versions
@tadd Redisクライアントは上げかけてパフォーマンスが出なくて下げました(今にして思うとGCが原因だったのかもしれません)。Rubyだけ3.1に下げて再現するか試したいんですけど、まだその機会がありません。そもそもなかなか再現してくれないんですよねートホホ
@zundan なるほどなるほど〜。別スレで髭の山男さんが出てくる方のはFixedってあっておっ?と思ったのですが、YJITのバグなのでまた別でしたね><
@zundan ぜんぜん問題を追わずに雑にバグを検索してみました。登録済みでそれっぽいのが3つくらい。
https://bugs.ruby-lang.org/projects/ruby-master/search?utf8=%E2%9C%93&q=marshal&scope=&all_words=&all_words=1&titles_only=&issues=1&attachments=0&options=1&commit=%E6%A4%9C%E7%B4%A2
まずはBackport済みだけど3.2.1には間に合わなかったという、とても怪しそうなのが一つ。ですがこれは、Regexpだけかな。別のものでも同じことが起きてるなら、とは思いますけど。
https://bugs.ruby-lang.org/issues/19439
次はまだOpenでBackport: REQUIREDのままのもの。TYPE_EXTENDEDならば全部関係しそうではあります。
https://bugs.ruby-lang.org/issues/19427
https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/7ddcee5928d8a98337077d5a5ee61136ec84a993/diff
最後は関係薄そうですが、パフォーマンス遅くなってタイムアウトの結果でnil、とかは無くもないかも、くらいです。
https://bugs.ruby-lang.org/issues/19424
という雑なチェックで申し訳ないのですが、ピンとくるのがあればラッキーかなと思います。
@tadd あざます!じっくり見てみます
@zundan いえいえ、なんかちゃんと読んでなくてすみませんという感じで。taxreturnが呼んでいるのです。(ただしreturn額が負)
@zundan 言わずもがなかもしれませんが、つまりは手パッチのコースですね……その方が楽になるならですが、部分的にコードを書いて作ったニセのlibrubyをLD_PRELOADで上書き、とかはできなくはないですが、たぶんHeroku的には(安全上もデプロイ方法の煩雑さでも)無理かなと思います。
@tadd ありがとうございました〜。どれも再現性が高そうで、単体では無関係そうな感じでした。
このエラーは弊ぼっちでは月に1度くらい(たぶん正確にはキャッシュされたStatus/トゥート 1個)しか起きていません…とか書いてると、Redisに書く側のrace conditionとかに原因があるのかもしれないと思えてきました。