turboからのリクエストに対してリダイレクトするときに気をつけること

  • turboを利用していて、destroyアクションでリダイレクトを使うときステータスコードを明示的に303(see other)にしないと、リダイレクト先にDELETEメソッドでアクセスしてしまうというハマりポイントが有る
  • Railsの現時点でのデフォルトのリダイレクト時は302なので、redirect_to @project, status: :see_otherのように明示的にステータスコードを指定するとGETでリダイレクトする
  • なぜこのような仕様になっているかというと、fetch APIがそういう仕様だから
  • fetch APIがなぜこのような仕様になっているかというと、302の仕様になるべく沿おうとしているから(要出典)
    • 302 Found - HTTP | MDN
    • 仕様書ではリダイレクトの際にメソッド (と本文) を変更しないよう要求していますが、すべてのユーザーエージェントが準拠している訳ではありません (まだこの種のバグのあるソフトウェアが見つかるでしょう)。従って、 302 コードは GET または HEAD メソッドへのレスポンスのみに使用し、 POST メソッドのままリダイレクトする場合は代わりに 307 Temporary Redirect (こちらでは明確にメソッドの変更が禁止されている) を使用することが推奨されています。

    • chromeなどのブラウザでは、GETとPOST以外のメソッドでアクセスした場合は同じメソッドをリダイレクト先にも適用する、という実装になっている模様
  • なので仕方ないっちゃ仕方ないんだけど、既存のアプリケーションに対してturboを採用しようとした場合には結構な負担になりますね
  • Rails側はこの仕様を緩和するための変更がいくつか入っていたり提案されていたりする

ぼくらは結局どうしたらいいんですかね

  • turbo-rails経由でPATCHやDELETEを発行しているぶんには気にせず302で問題なさそうではあるけど、気づかずにfetch APIを直接実行したときのことを考えるとサーバ側でも対応しておきたい
  • Add redirect_code_for_unsafe_http_methods config by jonathanhefner · Pull Request #45393 · rails/rails 相当のものをモンキーパッチして、redirect_to時のステータスコードをデフォルト303に一律で変えてしまうのが楽そうかな〜と思っています

Beats Studio Proを買ったけど返品した

ここ数年はずっとBoseのquiet comfort35を使っていて、音質などには不満は特にありませんでした。

でも複数pcを切り替える時AirPodsなどと比べると面倒*1。あとは2台同時にペアリングしている時に音を出したいデバイスではない方がアクティブになり、そちらのBluetoothを明示的にオフにしないと狙った方で音が出せなくてイライラする状況があったり。だんだんとこの状況を打破したいな、という気持ちが高まってきました。

AirPods max2がリリースされるのをずっと待っていたのですが、あと1年くらいは発売がなさそうな雰囲気。そこでApple傘下のBeats Studio Proなら発売間もないしいいんじゃないか、と思いポチってみました。

使ってみた感じとしては、Boseに感じていた諸々の不満が解消されていていいじゃん、となったのですが頭への締め付けがきつくて長時間つけていると辛い…。これまでヘッドホンの締め付けを気にしたことがなかったので盲点でした。やむなく返品することに。

Apple storeで購入したので返品処理がスムーズにできたのは不幸中の幸いでした。

もうちょっとBoseを使い続けるか、AirPods max1を買ってしまうかが目下の悩みどころです。

*1:仕事柄pcを複数台使っているので切り替える機会が多く面倒に感じやすい

テストでWebsocketのsubscribeが終わるまで待ちたい

  1. ページを開く
  2. ActionCableのブロードキャストを実行する
  3. ブロードキャストの結果画面が変更されるのを確認する

というテストがあるときに、2がActionCableの該当チャンネルのsubscribeより前に実行されてしまいテストが失敗する、という事象があった。

なのでsubscribeを待つためのヘルパーメソッドを書いてみた。asyncアダプタ想定で、subscriber情報を持っているハッシュの個数が0より大きくなればsubscribe完了しているだろう、と判断した。

  def wait_for_websocket_connection
    Timeout.timeout(Capybara.default_max_wait_time) do
      until ActionCable.server.pubsub.send(:subscriber_map).instance_variable_get(:@subscribers).count > 0
        sleep 0.5
      end
    end
  end

だいぶナイーブな実装なのでうまく動かないケースもありそうだけど、とりあえずこれで。

bullet的なN+1自動検知gemのprosopiteが気になる

user = User.first
token1 = user.user_token
token2 = UserToken.find_by(user: user)
  • たぶんprosopiteはN+1と判定する
    • のでzero false positives / false negatives
    • 本当に?
    • 手元でRailsアプリケーション作って試してみよう、と思ったのだけどsqlite3は対応してなさそうなのでまた今度試してみます…

CLionでCRubyのコードを読む

  • CLionでCRubyのコードをただ開いただけだとうまくコードジャンプが効かない
  • どうやったらええんや…となっていた
  • Makefileを開くと↓のように、「Makefileプロジェクトのロード」が表示されるのでロードするとそれっぽいジャンプができるようになった
  • これでCRubyのコード読みやすくなりそう

RubyがもっとCoWフレンドリーになりそう

  • Implement Process.warmup by byroot · Pull Request #7662 · ruby/ruby
  • Feature #18885: End of boot advisory API for RubyVM - Ruby master - Ruby Issue Tracking System
  • Ruby本体にProcess.warmupというメソッドが追加される
  • アプリケーション側でbootが終わったぞ、というときに使う
  • unicornやpumaなどのpreforkするタイプのアプリケーションはforkするので、そのタイミングでいい感じにCoWフレンドリーになるようにしたい
    • 例えば ko1/nakayoshi_fork を使うとforkの時点でGCを4回実行*1して、すべてのオブジェクトを古い世代にすることでCoWフレンドリーにする
      • オブジェクトが何回GCを生き延びたか、というフラグを持っているのでなにもしないとGCのタイミングでオブジェクトが書き換わり、親プロセスと子プロセスで差分ができてしまう
    • nakayoshi_fork自体は単純に4回GCを実行している
    • けど、Rubyの内部に「いまいるオブジェクトを全部古い世代にする」APIがあればそれ使ったほうが効率的ですよね
    • あと「複数のワーカプロセスをforkで生成する」ケースだとforkにフックをかけるやり方だとforkする回数分同じ処理が実行されるので効率が悪い
    • そこでアプリケーションの起動が終わったタイミングでおもむろにProcess.warmupを実行し、そこでもろもろいい感じにCoWフレンドリーにしてやると良い
    • とりあえず上記PRではGC、GC.compact、いまいるオブジェクトを古い世代にするということをProcess.warmupのタイミングでやっている
    • 他にもメソッドや定数のインラインキャッシュをキャッシュに載せておく、とかもっと最適化の余地がある模様
  • Ruby3.3だとpumaやunicornのメモリ消費量が減る、といいう未来がありそう
  • Rubyの最適化、もうあらかた実施済みなのかな、と思ってたけどまだやる余地あるんですね…

*1:3回GCを生き延びたら古い世代になる認識なんだけどなんで4回なんでしょうね…?

searchkickを使ったテストをするときに気をつけること

  • RSpecでsearchkickを使うときにどう設定するかがREADMEに書かれている
  • ↑を見ると、searchタグを付けたときだけsearchkickが有効になるように読める
  • が、これは間違い
  • searchkickの提供するコールバックのオンオフをするためのフラグはThread.currentで提供されている
  • つまりテストのスレッドで該当のモデルを作ったときにはsearchkickのコールバックは発火しないが、system specでpumaがモデルを作ったときにはsearchkickのコールバックは発火する
    • これ直感的じゃないのでグローバルにコールバックのオンオフを設定できると嬉しい気もするが、system specでサーバ側の挙動をテストに合わせるのもどうなんだろう、となったのでいったんこういう挙動である、という認識のもとやっていくことにした