その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

ライブラリや言語のバージョンを継続的に上げるという営みについて

ここ10年以上「バージョンを上げる」ということは習慣的にずっとやり続けていることであったので、いざ突然「なぜ?」と問われるとその場でパッと答えられないことに気付きました。瞬発力の無さが情けない……もちろんその効能についてはしっかり理解している (はずな) のですが、ちゃんと整理しておいたほうが良さそうということで言語化しておこうと思います。

blog.smartbank.co.jp

個人の根源的な思いとしては、かつて上記の記事に書いた

最新バージョンの言語やライブラリ、追いたいですよね。バグが直っていたり、新しい機能が使えたり、パフォーマンスが改善されていたりと良いことづくめなことに加え、新しいバージョンを使っているとなんだか不思議と気分が良いものです。

というところに帰着するとは思いつつ、某所で id:masawada が整理してくれた

  • バージョンを上げ続けないと、どこかで上げるのに失敗した時に原因の切り分けが難しくなる
  • 世の中一般のバージョンと乖離すると、世の中の知見をそのまま取り込めなくなる
  • 素朴にバージョン上がってるほうが便利になっているはず

という内容が正鵠を射ているように感じています。

また id:stefafafan が指摘してくれた「(バージョンアップによる) エンバグ避けたいなら機能追加や改修もやめてメンテナンスモードにするしかない」という言葉も良いなと思っており、つまり成長させ続けなければならないサービスにおいては諸々のバージョンアップは避けられない (すべきである)、というところに落ち着くのであろうと思っています。

あるいは id:tomo_ari が言っていた

osyoyu [22:59]
Rubyのバージョンを上げたいのは、自分にとってはRubyが圧倒的に "自分のもの" だからだと思う、自分や知人が作った最新の機能が入ってるバージョンは当然使いたいよねみたいな
osyoyu [23:00]
MySQLやAuroraやLinuxについても、それなりに知っていることである程度近しい感情はあって、上げない理由がないようなところがある
osyoyu [23:01]
なので、対象をよく知ると良い説がある

というのも良いですね。これは「知らないものに対して抱く恐怖」にまつわる話題と同等であるという認識をしており、よく知らないもののアップグレードは怖いのでやりたくなくなってしまうため、その悪循環を断ち切る為には対象に詳しくなるのが良い、というのはその通りであると思います。

その他いただいていたおたよりについてはこちら:




色々あるとは思いますが、頑張っていきたいですね。

[追記]

実際には僕もそのようにやってはいて、それが冒頭のPostの文中で「仕草」と表現したものになるのですが、そのような荒技が可能なのは大半のメンバーの認識・意識が合っている場合に限る気はしており、そこをこう、上手くやりたいですよね。頑張りたいですね。

所属変更のお知らせ

2026年1月1日より、次の通り所属が変更されています *1。

前: 株式会社スマートバンク
現: タイムリープ株式会社

前職在職中は大変お世話になりました。ありがとうございました。


タイムリープ株式会社ではリモート接客プラットフォーム (サービス名: RURA) を中心としたプロダクト作りをやっています。小さなチームでプロダクト開発をするのはなんだかんだ久々な感じで良いですね。
製品については実際に既にお客様にもご利用いただいており、便利で良いものであるように思っています。提供しているものの中には実ハードウェアもあったりするのでそこが燃えるポイントでもあります。


自分はエンジニアなのでもちろんエンジニアリングを行うのですが、チーム作りのようなこともやっています。というかそっちの比重の方が今は高いまであり……という今までの自分のキャリアでやったことが無い状況になっています。

とにかくReactで書かれたフロントエンドアプリのスペシャリストを渇望しています。WebRTCを中心に据えたブラウザで動作する遠隔接客プラットフォームに少しでも興味・関心のある方、何卒ご連絡ください! 会話しましょう!!! ちなみにサーバーサイドはGoです。

以下、近況です:

*1:1ヶ月経ってから報告したほうが色々と安全であるという学びがあるため遅れての報告になりました

Bundler 4.0.0を実戦投入した

Bundler 4.0.0が出ましたね。めでたい。ということで早速4.0.0にアップグレードしてみました。

オフィシャルの情報としての4.0.0の差分およびアップグレードガイドは ^ を見ると良いです。

実際にやったこと
  • bundle up --bundler=4.0.0を実行してlockファイルを更新
  • bundle installの後にbundle binstubs bootsnapをするように追加 *1
  • bootsnapだけでは足りず、ridgepole等も含める必要があることに気付いたため上記をbundle binstubs --allを使うように変更
  • bundle lock --add-checksumsを実行してlockファイルにCHECKSUMSを追加

binstubsの処遇は若干難しく、アップグレードガイドには

The --binstubs flag would create binstubs for all executables present inside the gems in the project. This was hardly useful since most users only use a subset of all the binstubs available to them. Also, it would force the introduction of a bunch of most likely unused files into source control. Because of this, binstubs now must be created and checked into version control individually.

とあるため、使うexecutableに絞ってbundle binstubsを実行してスリムに保ってね、というモチベーションであるということは理解しつつも、既存の系に対して適用しようとすると「どのexecutableが必要なのか」がパッと自明にわからず、一旦は安全側に倒して--allを使って全生成することとしています。
これについては同僚のid:ohbaryeさんがruby-jpのSlackチャネルで質問してくれている *2 ので、今後何か進展があるかもしれません。

[追記]
> it would force the introduction of a bunch of most likely unused files into source control.
とあるように、生成されたexecutablesがバージョン管理の対象になって複雑性が増すことが懸念なのであれば、今回のような用途、つまりコンテナのビルドステップ内でbundle binstubs --allを実行するのはそのコンテナ内に生成物が閉じるので大きな問題ではないように思いました。
そもそもbundle installで依存ライブラリ自体はインストールされているので、binstubsでexecutableを生成しようがしまいがコンテナサイズにも大きな影響はなさそうです。
id:h-sbtさんからも「とりあえず最初の一歩としては--allを使うので良いのではないか」というアドバイスもいただきました。
[追記ここまで]

lockファイルにCHECKSUMSが追加される挙動がデフォルトになったとのことだったのでこちらも導入しました。

We shipped this security feature and turned it on by default, so that everyone benefits from the extra security assurances. So whenever you create a new lockfile, Bundler now includes a CHECKSUMS section.

Bundler will not automatically add a CHECKSUMS section to existing lockfiles, though, unless explicitly requested through bundle lock --add-checksums.

明にbundle lock --add-checksumsを実行しないと自動的にCHECKSUMSを加えてくれないとのことだったので一度だけ手動実行して適用しました。実に良い機能なので使わない手はないですね。


というわけで比較的スッとバージョンを上げることができて良かったです。どんどんやっていきましょう。

*1:コンテナ環境で動かしているのでDockerfileを編集しました

*2:https://ruby-jp.slack.com/archives/CM0DN2H28/p1764746486399459

補足: 履歴テーブル、今回はこう作りました 〜 Delegated Types編 〜

「Ruby on Railsのテーブル設計とトランザクション処理 LT Night」で話した内容のフォローアップです。主に懇親会で id:kamipo さんから現地でもらった質問を受けての補足となります。

speakerdeck.com

図中のオレンジ枠と緑枠のトランザクションを分けているのはなぜ? ログレコードが保存できてイベントレコードが保存できないことなんてなくない?

説明がスッポリ抜けていたのですが、ASPからwebhookを受けて諸々の処理をする部分は非同期処理になっているのでそもそもライフサイクルごと分かれていたのでした。つまり、webhookを受けたタイミングでログレコードを記録してからキューイングし、それを非同期的にイベント処理をするので必然的にトランザクションが分かれる。

それはそうとして、イベントを扱う処理中に不慮のクラッシュが発生した時にロギングが巻き込まれると後の調査タスクに影響するので、防御的に作っているというのはそうです。

なぜ非同期処理にしているの?

必ずしもオンラインでやる必要が無いため。キューイングして非同期でやったほうが何かと取り回しが良い (後回しにできるものはしておくと最適化の余地が出てくる)。

enrollment_intentsとconversionsは同じテーブルで扱って良かったのではないか?

enrollment_intentsはユーザーの参加意図を表現していて、conversionsはその参加意図からの実際の参加を表現している。データモデル的には同一に扱えるかもしれないが、行動実態に照らし合わせると別にしたほうが分かりやすい。データを発生させる主体に注目するとenrollment_intentsはユーザーの能動的行動、conversionsはASPからのwebhook受信、とそれぞれ異なるのでライフサイクル的に別物であるという捉え方ができる。
またサービスの特性上、enrollment_intentsのデータ量とconversionsのデータ量は大きく異なる (離脱があるため前者の方が明確に多い) という背景もあり、テーブルを分けているという実情でした。

Delegated TypesじゃなくてSTIで十分だったんじゃないか?

それは本当にそうかもしれない……とはいえ今後の拡張 (提携する事業者が増えるなど) を考えると各ステートのテーブルのcolumnや振舞いが増える可能性が十分にあるので拡張の余地を残すべくDelegated Typesを採用したという背景があります。

スライド中の補足にも書きましたがDelegated Typesだとクエリ数が増え、気を抜くとN+1製造機になるというconsもあるので、そのパフォーマンス面を考慮するとSTIを選択するというのもありかもしれません。

各ステートのテーブルにuser_idとか入れといた方が良さそうじゃない?

それは本当にそう!!!!!!!!!! 絶対に後で必要になるので今入れます。

off-topic: Ruby 4.0.0 preview2とZJITって速い?

まだ評価できるほどのトラフィックを流していませんが、俺は信じています。あとZJITはまだ性能評価の前段階にあるという認識です。俺は信じています。




その他にも、

  • RDBMSのカッチリしたテーブル構造とJSON型のcolumnを組み合わせたハイブリッドな構成は今となってはアリなのではないか
    • MySQLにもCHECK制約がやって来たのでJSONのvalidationã‚’CHECK制約により行えるし、pgであればpartial indexを使えばJSONであっても現実的にindexを張れる、などなどJSON型を取り巻く状況は以前とは変わっているから
    • それはそうとして適切な用途にはDynamoDBを使いたくはある (経験があるので)、新機軸としてDSQLも気になる
      • 用途に応じて保存するストレージを分けられるようになったというのは昨今のアーキテクチャの進歩ではないか
  • user tableをどのように作るか
    • 1つの巨大テーブルとして扱うか、適切にテーブル分割をするか、するとしてどのような基準で分割するか
    • æ‹¡å¼µ (例えばALTER時) が辛くなるのであれば分割のサインなのではないか、など
  • スライド中にワイプのようにER図を表示すると便利そうだと思って今回のスライドに入れてみた (36ページ目) けれど時間が無くてみなさんにお見せできなかった

などという話題がありました。楽しかったですね。

[追記]
補足の補足をいただきました:

DBのcolumn名や、何らかドメインの意味を持つ概念に `type` を名付けない方が良いのではないか

type そのもの、 あるいは _type のようなsuffixを持つ名前を変数や、構造体・クラスのメンバーや、データベースのcolumnなどに付けてしまうことがしばしばあると思うのですが、個人的にはあまりこれはやらない方が良いのではないかと考えています。

理由としては

  • type はいくつかのプログラミング言語において予約語になっており、そのセマンティクスにおいて特別な役割を果たすことが多い。
  • Ruby on RailsにおいてはDelegated Typesという機能において、 _type というcolumnは特別な意味を持つ (もちろんアプリケーションコード側でDelegated Typesであるという宣言をしなければ副作用は無いのですが)。

というものがあると思っており、そういった概念との衝突を避けるために特別かつ強い理由が無い限りは type という命名は避けようというような気持ちで日々を過ごしています。
代替としては kind や method などが使えそうであると思っておりこれらを採用することが多いです。methodは別の概念との衝突がある場合もありそうですが、そこは文脈に沿って、という感じですかね。どうしても type でしか表現できないものはあると思うのですが (例えば言語処理系を作っていて、本当に「型」を取り扱う必要がある場合など)、そういった場合は頑張りましょう。


かつてRustで `typ` という変数名を乱発していたことに対する戒めを込めています。

Kaigi on Rails 2025でasync gemを使ってSSE機能をRails Appで作るという話をしました #kaigionrails

speakerdeck.com

発表資料はこちらです。以下に資料に入れそびれた・話しそびれたことを以下に記しておきます。

実際のところSSEを導入したのはなぜなのか

今回例示したような「長い時間がかかる処理の進捗報告・完了通知」のような機能はポーリングでも実現可能なものであり、我々のプロダクトでも当初はポーリングを前提として設計がされていたのですが、以下のような理由からSSEを採用するに至って今回の発表に繋がっています:

  • ポーリングよりも基本的にはユーザー体験が良くなるはず (ポーリングの場合は変化までに高々インターバル秒かかる可能性がある) *1
  • 技術的な挑戦 (おたのしみ)

後者が地味に大事だと考えていて、何か新しい機能やサービスを作ったりする時に「既存のやり方」のみで固定化するのではなく *2、技術的に新しい取り組みをすることによって技術面・組織面での「実力」を高めたいというモチベーションが背景にありました。あと技術的に新しいことをやると純粋に楽しいですからね (楽しいだけではもちろん駄目ですが……)。僕はこれを「おたのしみ」と呼んでいて、新しいものを作る時にはできる限り取り組みたいと考えています。

Active Recordのコネクションをfiber nativeにする方法

スライドに書き忘れていました。

config.active_support.isolation_level = :fiber としてあげると、スライドの49ページ目に書いたようなworkaroundが不要になります *3。

我々も当初この設定を入れようとしていたのですが、既に動いているそこそこの規模のシステムのisolation_levelを変更することに一定のリスクを感じたので見送っていたのでした。とはいえ多くの場合では問題が起こらない気もしますし (もちろん要検証)、少なくとも新たにrails newする時には問題無いと思うので、この設定でトライするのが良いと考えています。

MyModel.with_connection が使える

@ioquatixさんから発表後に教えてもらったのですが、表題のように MyModel.with_connection としてconnectionをborrowできるようになっているとのことです。べんり。

詳細については@ioquatixさんが書いてくださっているgistを参照してください: Show how `with_connection` and `lease_connection` interact. · GitHub

余談ですが、上記gistにあるように config.active_record.permanent_connection_checkout = :disallowed を設定に書いておくのは良さそうに思いました。

Rack 3以降は ActionController::Live を使わなくてもストリーミングレスポンスを返却できる

こちらについても@ioquatixさんから発表後に教えてもらいました。表題の通り、以下のように記述するとSSEでのレスポンスが可能なようです:

github.com

上記の例はFalconのものとなっていますがPumaでも問題無く動くとのことでした。これは知らなかった、良いですね。




ご覧の通り、async gemや数々の便利なライブラリ・ツールを作るなどのご活躍をされており、Kaigi on Rails 2025のキーノートスピーカーの@ioquatixさんから色々と教えていただけました。ありがとうございます。

テックカンファレンスはこういう交流ができるというのが良いところですね。

*1:ポーリングのほうが仕組みとしてシンプルであるというのは正。そしてSSEであってもコネクション数などの観点でのパフォーマンス優位性はそこまでないので……

*2:もちろん「型」を持っておくのは重要なのですが

*3:https://railsguides.jp/configuring.html#config-active-support-isolation-level

[令和最新]Resemblaをビルドして動かす方法

github.com

類似文字列検索ライブラリであるところのResemblaですが、利用に際してはビルド済みのパッケージが配布されていないため自分でビルドする必要があります。
が、Wikiに書かれているインストールドキュメントがCentOS 7のものになっており *1 、現代の環境で動かすにあたってはちょっと工夫が必要……ということで、2025年現代の環境で動作するDockerfileをここに共有します。

gist.github.com

ポイントとしては

  • mecab-ipadicではなくmecab-ipadic-utf8 を使う (thanks id:tomo_ari and id:ssig33)
  • icuのバージョンは59.1で固定
  • grpcのバージョンはv1.2.5で固定
    • 新しめのgccだとgettidがバッティングしてgrpcがビルドできないので、コードから除去するワークアラウンドを入れる
    • 新しめのgccだと警告出まくってビルドできないのでC++標準バージョンを下げつつ警告を無視してmake: make CFLAGS="-w -std=c11" CXXFLAGS="-w -std=c++14"
  • Resemblaのビルドに際しても新しめのgccだとエラーが出るのでcstddefã‚’includeするようにワークアラウンドを入れる *2

という感じでしょうか。このようにしておくと動き、grpcを使ったインターフェイスも動作するところまで持ってゆくことができます。

mecab-unidicをインストールしたい場合、こちらにもちょっとコツが必要なので頑張りましょう:

github.com

以上です。ご活用ください。