Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: Rails 7.1.0.rc1と7.1.0.rc2がリリース、SQLite3コンフィグの最適化ほか(20131004)

こんにちは、hachi8833です。7.1.0.rc1とrc2が立て続けにリリースされました。もしかすると今週の金土に開催される開催される第1回Rails World 2023の場で正式版がリリースされるかも?

参考: Release 7.1.0.rc1 · rails/rails

参考: Release 7.1.0.rc2 · rails/rails

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Rails: 先週の改修(Rails公式ニュースより)

🔗 Active Jobのリトライオプションにwait: :polynomially_longerを追加し、wait: :exponentially_longerを非推奨化

動機/背景

このプルリクを作成した理由は、wait: :exponentially_longerというオプション名が、ジョブを再試行する頻度について誤解を招くため。

Active Jobのバックオフ戦略は「多項式時間バックオフ(polynomial backoff)」である。この場合は四次関数時間(quartic backoff)バックオフとも言えるが、指数関数時間バックオフではない。

自分のチームが取り組んでいるアプリの一部(フランスの公共サービス予約管理システム)は最近、バックグラウンドジョブがタイムアウトして再試行を大量に繰り返し、キューがオーバーフローしたために一時的な停止状態に陥ったことがあった。

このインシデントについてポストモーテムを開催し、ジョブがどのぐらい速やかに再スケジュールされたかを調査した。最初の数回を試してみたところ、驚いたことに指数関数時間バックオフよりも遥かに短い時間でリトライしていた。これをきっかけにこの問題を調査した。

詳細

このプルリクは、ジョブが多項式時間バックオフで再試行されることを明示するためにオプション名を変更し、後方互換性のために従来のオプション名も引き続き動くようにする。

多項式時間バックオフと指数関数時間バックオフの違い

Active Jobで実装されているリトライの遅延時間はretry_count**4で多項式的に増加するが、古典的な指数関数時間の遅延は、通常2**retry_countのように指数関数的に増加する(このGoogle Cloudのドキュメントページなどを参照)。

指数関数時間バックオフは、最初の数回の試行の初期値が(多項式時間の場合と)一致しやすくなるように4**retry_countとして実装されることもある(自分たちはこの実装を期待していた)。

これらをまとめると、それぞれの戦略がリトライ間で待機する時間は次のようになる(ジッターを除く)。

retry_count:          1,   2,  3,   4,   5,   6,   7,  8,  9,   10,  11, 12, 13,  14,  15,  16,  17,  18, 19, 20,
retry_count**4 delay: 1s, 16s, 1m,  4m, 10m, 22m, 40m, 1h, 2h,  3h,  4h, 6h,  8h, 11h, 14h, 18h, 23h, 1d, 2d,  2d,
4**retry_count delay: 4s, 16s, 1m,  4m, 17m,  1h,  5h,18h, 3d, 12d, 49d, 194d,
2**retry_count delay: 2s,  4s, 8s, 16s, 32s,  1m,  2m, 4m, 9m, 17m, 34m, 1h,  2h,  5h,  9h, 18h,  2d, 3d, 6d, 12d,

なお、興味深いことにAction Cableの場合はリトライ戦略で真の指数関数時間バックオフを使っている(詳しくは以下を参照)。

これは命名ミスか、それとも本当のバグか?

これをバグと見なすべきか単なる命名エラーと見なすべきかで悩んだ。言い換えれば、期待と実装のミスマッチを是正するために、コードを2 ** retry_count(それとも4 ** retry_countか?)に変更するべきか、それともオプション名を変更すべきかがわからなかった。

少し調べてみたところ、主要なRubyライブラリのほとんどで多項式時間バックオフによるretry_count**4が事実上の標準になっていることに気付いた(興味があれば次のセクションで詳しく説明してある)。

さらに、多項式時間バックオフと指数関数時間バックオフのどちらが優れているかを判断するのは真の難問であるらしい。一般的なRailsアプリケーションに適用できる明確な答えが存在するとは思えない。

しかしながら、オプション名から期待できるものを管理し、利用しているバックオフ戦略を明確にすることについては合理的だと思える。

そういうわけで、「壊れてなければ修正しない」精神に基づけば、自分にとって最も理性的な選択肢は、オプション名を明確なものに変更するが、実装(および後方互換性)を維持することである。

おそらくだが、Active Jobは真の指数関数時間バックオフ戦略を提供する組み込みオプションを新たに提供すべきではないか(これは別のissueで取り上げるべきかもしれない)。

追加情報

多項式時間バックオフと指数関数時間バックオフの混乱は、Rubyコミュニティ全体に大きく広がっているらしい。驚いたことに、他のRubyライブラリにもretry_count ** 4 backoffを「指数関数時間バックオフ」と呼んで使っているものいくつもあることが判明した。歴史的な興味としてこの起源を探ってみたところ、以下を見つけた。

後にsidekiq、sneaker handler、resque-retryでも同様の実装が出現している(2016年にActive Jobに追加されるよりも前の時代)。

自分は、この混乱が生じている多くのプロジェクトでも同様のプルリクをオープンする予定。

なお数学マニアなら、指数関数時間の複雑性で計算を解けるアルゴリズムが、多項式時間でも解けるかどうかを判定するのは真の難問であると指摘したくなるかもしれない。
同PRより


つっつきボイス:「少々長めのプルリクメッセージです」「ややこしいけど、どうやらActive Jobのリトライ戦略のオプションに関連する修正ということかな」「リトライのwait: :exponentially_longerの振る舞いが、その名に反して指数関数時間のバックオフではなく多項式時間のバックオフになっていたので、今後は適切なオプション名であるwait: :polynomially_longerを使ってねということか」「wait: :exponentially_longerは非推奨になったけど、振る舞いを変えていないのは影響が大きいからでしょうね」

# activejob/lib/active_job/exceptions.rb#L59
      def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil, jitter: JITTER_DEFAULT)
+       if wait == :exponentially_longer
+         ActiveJob.deprecator.warn(<<~MSG.squish)
+           `wait: :exponentially_longer` will actually wait polynomially longer and is therefore deprecated.
+           Prefer `wait: :polynomially_longer` to avoid confusion and keep the same behavior.
+         MSG
+       end
        rescue_from(*exceptions) do |error|
          executions = executions_for(exceptions)
          if attempts == :unlimited || executions < attempts
            retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions, jitter: jitter), queue: queue, priority: priority, error: error
          else
            if block_given?
              instrument :retry_stopped, error: error do
                yield self, error
              end
              run_after_discard_procs(error)
            else
              instrument :retry_stopped, error: error
              run_after_discard_procs(error)
              raise error
            end
          end
        end
      end

参考: 多項式時間 - Wikipedia
参考: 指数関数時間 - Wikipedia

「実装で素朴にリトライを立て続けに繰り返すとシステムに支障が起きるので、たとえば最初は1秒待ってリトライしたら次は2秒、次は4秒、次は8秒...みたいにリトライの待ち時間を逓増させることがよく行われていますよね」「その待ち時間をどう増やしていくかという戦略がオプション名と合っていないのを問題にしているのか」

参考: リトライ処理の効率的アプローチ「Exponential Backoff」の概要とGoによる実装 - Qiita

「プルリクにもあるこの表がわかりやすいと思います↓: 一番上は待ち時間を素朴に1秒ずつ増やしているけどリトライが頻繁すぎてしまう、2番目のretry_count**4は待ち時間が急速に増えていく多項式時間バックオフ、3番目と4番目の指数関数時間バックオフだともっと急速に待ち時間が増えていく」「2番目のretry_count**4と3番目の4**retry_countは最初の5つの待ち時間が似ていますね」

retry_count:          1,   2,  3,   4,   5,   6,   7,  8,  9,   10,  11, 12, 13,  14,  15,  16,  17,  18, 19, 20,
retry_count**4 delay: 1s, 16s, 1m,  4m, 10m, 22m, 40m, 1h, 2h,  3h,  4h, 6h,  8h, 11h, 14h, 18h, 23h, 1d, 2d,  2d,
4**retry_count delay: 4s, 16s, 1m,  4m, 17m,  1h,  5h,18h, 3d, 12d, 49d, 194d,
2**retry_count delay: 2s,  4s, 8s, 16s, 32s,  1m,  2m, 4m, 9m, 17m, 34m, 1h,  2h,  5h,  9h, 18h,  2d, 3d, 6d, 12d,

「ちなみに、四次関数時間のバックオフ(quartic backoff)と書かれているのを見たときに最初quadratic(二次関数時間)かなと思ったけど、コードがretry_count**4なので四次関数でした」「たしかに」

「Rubyのライブラリにも多項式時間バックオフと指数関数時間バックオフの実装が取り違えられているものがあるとは知りませんでした」「プルリクの作者はこれらも是正するつもりなのか、すごい」「ところでプルリク最後の"多項式時間でも解けるかどうか"云々はP≠NP予想のことですよね」

参考: P≠NP予想 - Wikipedia

🔗 SQLite3の結合演算子||をアダプタのデフォルト関数に渡せるようになった

SQLite3アダプタで、結合演算子||を含むデフォルト関数を処理できるようになった。

従来は、デフォルト関数で"'Ruby ' || 'on ' || 'Rails'"のように静的な文字列が生成されていた。
改修によって、"Ruby on Rails"を適切にアダプタに渡して利用できるようになった。

change_column_default "test_models", "ruby_on_rails", -> { "('Ruby ' || 'on ' || 'Rails')" }

動機/背景

SQLite3をproduction環境で利用する動きが広まっているが、Active RecordのSQLite3Adapterは、SQLiteエンジンのパワーと機能を十分に活用できていない。MySQLAdapterPostgreSQLAdapterの機能と互換にする手始めとして、SQLite3Adapterをレベルアップさせるためにいくつものタスクに取り組み始めている。

詳細

手始めに、最初に見つけたもっともシンプルな改善に手を付けることで、RailsとActive Recordへの機能のアップストリームにはずみを付けることにする。SQLite3では、CONCAT()関数ではなく結合演算子||を提供しているが、現在のアダプターはこの演算子を動的なデフォルト関数として認識しない。
同PRより


つっつきボイス:「このプルリクはSQLite3のアダプタに文字列結合用の||演算子を渡せるようにしたシンプルな改修ですが、これは前哨戦で、次のプルリクがメインのようです」

参考: SQL Language Expressions

🔗 SQLite3でsupports_insert_returning?をサポート

supports_insert_returning?AbstractAdapter)を完全に実装することで、SQLite3アダプタでも自動生成カラム(#48241)とカスタム主キーをサポートする。

動機/背景

上の#49287の修正を踏まえて、これは自分がSQLite3AdapterMySQLAdapterPostgreSQLAdapterと同等の機能を持つようにするために行う最初のプルリクとなる。

SQLite3は機能の豊富なデータベースエンジンであり、production環境での利用は増加の一途をたどっている。自分たちは、開発者にSQLite3の機能範囲とスコープを提供するためにRailsでのサポートを必要としている。

詳細

#48241では、Active Recordモデルの属性をデータベースのデフォルト値で自動的に埋める素晴らしい機能が導入された。当初この機能はPostgreSQLAdapterのみで実装されていた。
この振る舞いのためのSQLの基盤となる重要な機能はRETURNINGキーワードだが、これはSQLite 3.35.0 (2021-03-12)以降でサポートされている(参考)。

このプルリクでは、以下を実装することでSQLite3AdapterRETURNINGキーワードをフルサポートする。

  • ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements#sql_for_insert
  • ActiveRecord::ConnectionAdapters::SQLite3::DatabaseStatements#returning_column_values
  • ActiveRecord::ConnectionAdapters::SQLite3Adapter#supports_insert_returning?
  • ActiveRecord::ConnectionAdapters::SQLite3Adapter#return_value_after_insert?
  • ActiveRecord::ConnectionAdapters::SQLite3Adapter#use_insert_returning?

いくつかのメソッドについて調整も行った。

追加情報

この取り組みは、#49287から始めた。
同PRより


つっつきボイス:「以前#48241で、7.1にPostgreSQLのRETURNINGを使った新機能が入ることになったんですが(ウォッチ20230621)、プルリク作者の@fractaledmindさんによると、新しいSQLite3でもRETURNINGが使えるとのことなので、このプルリクでSQLite3でも使えるようにしたそうです」「へ〜!」

「Railsブログの更新情報にはありませんでしたが、同じ@fractaledmindさんによる#49349も既にマージされています」「これは?」「SQLite3のコンフィグを見直してパフォーマンスを平均で2倍向上させたんだそうです」「SQLite3にめちゃ強そうな人だ」

「このプルリクによると、近年productionでのSQLite3の利用が増えているんだそうです」「本当かしら?自分はSQLite3をproductionでは使ったことないんですよね: 読み出し専用で使うとかならありかもしれないけど」「SQLite3はデータベースサーバーを立てずに使えますよね」「環境によってはSQLite3クライアントのライブラリもインストールしないといけなくなりますけどね」

参考: Sqlite & Rails in Production · The Ruby Dispatch -- fly.io Blog
参考: using sqlite3 as production database : r/rails -- reddit.com

「HerokuではSQLite3の利用が認められていないんですが↓、最近だとfly.ioなどでは使えるみたいですね」「いずれにしろSQLite3が用途に合うかどうかでしょうね」

参考: Heroku 上での SQLite | Heroku Dev Center


つっつきの後で#49349を読んでみました。

▶#49349(クリックして展開)

🔗 SQLite3アダプタのコネクション設定のパフォーマンスチューニング

Normal同期モードのWAL(Write-Ahead-Log: ログ先行書き込み)導入、ジャーナルサイズの上限設定、共有メモリバッファや共有キャッシュの設定の健全化によって、RailsアプケーションにおけるSQLite3のパフォーマンスを平均で2倍増しにする。

動機/背景

SQLite3を利用するRailsアプリケーションは、データベースが実行されるWebアプリケーションのコンテキストに対するチューニングが貧弱だった。現在のSQLite3のデフォルト設定は、後方互換性や本来の利用目的である組み込みシステムを対象に行われたものであり、Webアプリケーションでの利用に適していない。ありがたいことに、SQLite3はPRAGMA文で手軽に設定できる。

学術文献開発者の実体験、および私自身のブログにも詳しく書かれているように、SQLite3データベースを最低でも2倍のパフォーマンス向上を達成する最適化は簡単に行える。

このプルリクは、RailsのSQLite3コネクションのデフォルト設定を更新して、最新のRailsアプリケーションのコンテキストに合ったチューンナップを施す。

詳細

理解およびチューニングの必要なプラグマのうち、最も重要なのはjournal_modeプラグマである。SQLite 3.7.0(2010-07-21)以降、アトミックトランザクションをサポートするWAL(Write-Ahead log)が提供されている。これはWebアプリケーションにおける推奨ジャーナルモードである。SQLite3ドキュメントには以下のように書かれている。

WALを使うことで、ほとんどのシナリオにおいて著しく高速化される。
WALは、リーダー(reader)としてのコンカレンシーを増やし、ライターをブロックしない。ライターもリーダーをブロックしない。読み書きはコンカレントに行われるようになる。

アプリケーションでWALを使うようにすれば、同期の設定を緩和するのが理にかなっている。

synchronous=NORMAL設定は、WALモードで動作するほとんどのアプリケーションに適している。

NORMAL同期モードにすると、SQLite3がディスクに保存する頻度が、書き込みごとに保存する場合よりも少なくなる。SQLite3は、ディスクへの書き込みを独自のアルゴリズムで「最も重要な瞬間」に行うようになっており、同期をwal_autocheckpointページ単位で行う(デフォルトでは1000ページ)。つまり、wal_autocheckpointプラグマが変更されると、Normalモードの同期はその数ページ後に発生するようになる。したがって、速度を稼ぐために耐久性と引き換えにアグレッシブな方法を使うことになる。
ただし、SQLite3は耐久性低下を軽減するさまざまな処理も行っているので、実際には極端なエッジケースである。実際SQLite3は、潜在的なデータ損失はOSやファイルシステムの故障時にしか発生しないようになっており、プロセスがクラッシュしてもデータ耐久性に影響しない。したがって、この最適化は99%のケースを対象にしており、1%のケースは対象にしていない。これはRailsアプリケーションに適していると思われる。


journal_size_limitプラグマは、ディスクファイル内で保持するWALデータの量をSQLite3に指定する。
-1(デフォルト値)はデータ量に上限を指定しないことを意味するので、このディスクファイルは無制限にサイズが増加する。この設定は望ましくない。ディスクがログファイルでいっぱいになってアプリケーションのダウンタイムを経験したことがある人ならば、ファイルサイズを無制限にすると頭痛の種にしかならないことはおわかりだろう。適切なサイズでファイルサイズを制限する必要がある。


次はmmap_sizeプラグマ(名前の短縮が甚だしいので注意)。この設定は「メモリマップI/Oでアクセスされるデータベースファイルの最大バイト数」を制御する。詳しく説明すると長くなってしまうが、要はメモリマップI/Oを有効にすると、SQLite3が複数のプロセス間でデータを共有できるようになる。メモリマップはPostgreSQLのバッファプールと同様の役割を果たすので、無効にするのではなく、デフォルトのPostgreSQLバッファプール(128MB)と同じ安全な値に設定する必要がある。


最後のcache_sizeプラグマは、「SQLiteが一度に開くデータベースファイルごとにメモリ内で保持してよいデータベースディスクページの最大数」を設定する。
-2000(デフォルト値)は負の数であり、SQLite3は負の値をバイト数の制限と解釈する。
正の数を指定すると、SQLite3はこれをページ数の制限と解釈する。
デフォルトの上限は、ページ数にかかわらず約2MB(2,048,000バイト)になる。
キャッシュサイズが大きくなってもページ間で分割されないようにするには、正の値を指定してキャッシュ制限をページ番号に設定すべき。自分はcache_sizeとして2000ページを指定することを推奨する。この場合、デフォルトのページサイズ4,096バイトにおけるキャッシュ上限は約8MB(8,192,000バイト)となる。
#49349より

参考: ログ先行書き込み - Wikipedia
参考: メモリマップドI/O - Wikipedia

🔗 ドキュメント更新

🔗 locals:local_assignsのドキュメントを改善

動機/背景

新しいRuby構文のおかげで、パーシャルのローカル変数で興味深い可能性が示された。

詳細

local_assignsのドキュメントに、Ruby 3.1のパターンマッチング代入との統合の可能性を追加した。

Action Viewの概要ガイドに、render呼び出しにlocals:オプションの説明を記述する方法を追加し、local_assignsメソッドについても追加した。また、Ruby 3.1のパターンマッチング代入との統合の可能性についても概要を追加した。
同PRより


つっつきボイス:「ビューのパーシャルに渡すlocals:にアノテーションを付けられる機能が7.1で入る予定なんですが(ウォッチ20220822)、そのあたりの機能のドキュメントが更新されました」「このアノテーション機能のこと知らなかったけど、よさそう!」「このときのつっつき会でも盛り上がりました」

<%# issues/_card.html.erb %>
<%# locals: (title: "Default title", comment_count: 0) %>
<h2><%= title %></h2>
<span class="comment-count"><%= comment_count %></span>

🔗 strict_loadingのドキュメントを更新

APIドキュメントに単数形の関連付けのコード例を追加。

APIドキュメントの:n_plus_one_onlyモードのコード例で、コメント読み込み時にエラーがraiseされていたのを修正。

strict_loading!をガイドに追加。

APIドキュメントのコード例にto_aを追加(関連付けの読み込みでエラーをraiseするため)
同PRより


つっつきボイス:「こちらもドキュメントの更新で、strict_loading!をレコードで直接呼べる機能とmode: :n_plus_one_onlyを指定するとN+1が発生したときだけraiseする機能がドキュメント化されたそうです」「お〜、ちょうど今N+1クエリをつぶしまくっているところなんですが、7.1がリリースされないと使えないんだろうか?」

つっつき後に、strict_loading!n_plus_one_onlyはRails 6.1から使えると教わりました(#37400)。

# guides/source/active_record_querying.md#1782
user.strict_loading!(mode: :n_plus_one_only)
user.address.city # => "Tatooine"
user.comments.to_a # => [#<Comment:0x00...]
user.comments.first.likes.to_a # raises an ActiveRecord::StrictLoadingViolationError

Rails: Active Recordメソッドのパフォーマンス改善とN+1問題の克服(翻訳)

🔗Rails

🔗 rc1で標準出力のログ情報が不足する問題(rc2で修正済み)


つっつきボイス:「Rails 7.1の全Changelogを読みながら7.1beta1やrc1で動かせるところを動かしているときに、そういえば以下のようなCompleted 200 OK↓とかがいつの間にか表示されなくなっているなと思っていたら、rc1で同じ問題を踏んでいた人を上のDiscussionで見つけました」

Completed 200 OK in 979ms (Views: 374.4ms | ActiveRecord: 87.6ms | Allocations: 70836)
▶rc1実機の標準出力(クリックで展開)
bin/rails s
=> Booting Puma
=> Rails 7.1.0.rc1 application starting in development
=> Run `bin/rails server --help` for more startup options
DEPRECATION WARNING: !render.view_component is deprecated and will be removed from ViewComponent 4.0.0 (Use the new instrumentation key `render.view_component` instead. See https://viewcomponent.org/guide/instrumentation.html) (called from <main> at /Users/hachi8833/deve/rails/rails71trial/config/environment.rb:5)
Puma starting in single mode...
* Puma version: 6.4.0 (ruby 3.2.2-p53) ("The Eagle of Durango")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 14920
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
Started GET "/" for ::1 at 2023-09-28 20:22:11 +0900
  ActiveRecord::SchemaMigration Load (18.2ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
  Pattern Count (19.7ms)  SELECT COUNT(*) FROM "patterns"
  ↳ app/controllers/proofreads_controller.rb:5:in `home'
Started GET "/assets/tailwind-85f1c8e82d5a5bdc07d4fae87d65f26698058d73.css" for ::1 at 2023-09-28 20:22:13 +0900
Started GET "/assets/es-module-shims.min-295257ea6e233f6a8d82987e4107c1b3f9ed62ea.js" for ::1 at 2023-09-28 20:22:13 +0900
Started GET "/assets/stimulus.min-4b1e420eb07f8afa5ce3620fe38b5e2d411bc3ec.js" for ::1 at 2023-09-28 20:22:13 +0900
Started GET "/assets/turbo.min-522dcb4760c129e3820867b4b126a3904bacf96b.js" for ::1 at 2023-09-28 20:22:13 +0900
/Users/hachi8833/.anyenv/envs/rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/rack-3.0.8/lib/rack/file.rb:5: warning: Rack::File is deprecated and will be removed in Rack 3.1
Started PATCH "/check" for ::1 at 2023-09-28 20:22:26 +0900
  Pattern Count (7.7ms)  SELECT COUNT(*) FROM "patterns"
  ↳ app/models/pattern.rb:29:in `fetch_regex'
  Pattern Load (4.1ms)  SELECT "patterns"."id", "patterns"."regex", "patterns"."comment", "patterns"."posi_sample", "patterns"."nega_sample", "patterns"."hit_count", "patterns"."display_name", "patterns"."memorandum", "patterns"."category", "patterns"."color", "patterns"."created_at", "patterns"."updated_at" FROM "patterns" ORDER BY "patterns"."updated_at" DESC LIMIT $1  [["LIMIT", 1]]
  ↳ app/models/pattern.rb:29:in `fetch_regex'
  Pattern Pluck (0.8ms)  SELECT "patterns"."id", "patterns"."regex", "patterns"."comment", "patterns"."category", "patterns"."color" FROM "patterns"
  ↳ app/models/pattern.rb:30:in `block in fetch_regex'
  Pattern Update All (6.3ms)  UPDATE "patterns" SET "hit_count" = COALESCE("hit_count", 0) + $1 WHERE "patterns"."id" IN ($2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)  [["hit_count", 1], ["id", 4], ["id", 6], ["id", 1], ["id", 10], ["id", 2], ["id", 3], ["id", 5], ["id", 7], ["id", 8], ["id", 9], ["id", 11], ["id", 12], ["id", 13], ["id", 14]]
  ↳ app/models/highlight_match.rb:39:in `proofread'
^C- Gracefully stopping, waiting for requests to finish
=== puma shutdown: 2023-09-28 20:22:52 +0900 ===
- Goodbye!

「どうやら以下のプルリクでActiveSupport::BroadcastLoggerを導入したときに、ログ情報が標準出力から欠落していたようです↓」「ありゃ、これは直して欲しいですね」


その後rc2で以下の修正がマージされ、標準出力にレスポンスが出力されるようになりました😂。

$ cat Gemfile|rg 7.1.0
gem "rails", "~> 7.1.0.rc2"
# ...
  Rendered layouts/_nav.html.erb (Duration: 4.3ms | Allocations: 1111)
  Rendered layouts/_footer.html.erb (Duration: 0.5ms | Allocations: 163)
  Rendered layout layouts/proofreads.html.erb (Duration: 64.0ms | Allocations: 37943)
Completed 200 OK in 196ms (Views: 73.2ms | ActiveRecord: 23.9ms | Allocations: 70770)

🔗 SQLite3を使うRailsアプリを強化する(Ruby Weeklyより)


つっつきボイス:「上の@fractaledmindさんがSQLite3関連のプルリクにも貼っていた記事です」「SQLite3のチューニングにここまで情熱を注いでいるとは...すごい」「業務だとSQLite3の詳細部分をここまで追求する時間がなかなかなさそうですよね」


今回は以上です。

バックナンバー(2023年度第3四半期)

週刊Railsウォッチ: 7.1でバリデーションメッセージのアポストロフィ->カーリー置き換えが取り消しほか(20230928後編)

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Rails公式ニュース

Ruby Weekly


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。