Kaigi on Rails 2025
day1
opening keynote
dynamic!
継続して変化し続けること
最初から正解し続けるのは難しい
Rubyは動的な言語
IRB
ソフトウェアの仮説検証の最小単位
ハッピーパス
ふつうの利用者が期待する最もシンプルな操作ができるところまで
scaffoldに近い
イベントentity
UserがConferenceに登録したこと
DBとリソースの設計ができていれば簡単
集中するもの以外は保留する
すべてのカラムを定義しきろうとしない
イベントentityを見つけると集約ができて便利
メール送信コードはどこに置く
最もシンプルでうまくいくところに留める
そのときが来たらコードを変える
しっくりこなかったら戻す
変えたり戻したりするための仕組み
usersテーブルの話題
プロフィール・個人情報などは別のテーブルに分ける
2つ以上のテーブルを同時に更新したい
「利用者から見たプロフィール」リソースを更新する
ActiveModel as a PORO on Rails
ActiveSupport::CurrentAttributes
メアドの到達確認
「プロフィール更新」と「メアドの変更手続き開始」
EmailChangeRequest
メールをFormObjectから送る?
フォースが変わるとコードも変わることがある
ドキュメントでの意思疎通は難しい
動くソフトウェアで会話する
リリースしてからも動的に変化させる
アジャイルソフトウェア開発宣言
高度なUI/UXこそHotwireで作ろう
Railsはフロントエンド
Reactはstate一本
Stimulusのvalue state
Hotwireを使うとJavaScriptなしで済む、ではない
JavaScriptを取り入れることでよいユーザー体験につなげられる
「管理画面はHotwireで作り、ユーザー画面はReactで作ろう」
複雑なUI/UXはReactじゃないと作れない→本当に?
Basecamp
ReactとERB+jQueryを見分けられるか?
Next.js pages routerのページ遷移 vs Hotwire/TurboDrive
ローディングUI
SWR型のキャッシュ
prefetch
作り込まない限りHotwireのほうが速い
HotwireにReactを埋め込む
Stimulus
そもそも複雑なUI/UXとは?
ReactじゃなくてもVanilaJSやブラウザの機能で実現できるのでは?
React.jsの誕生ドキュメンタリー
リアルタイムダッシュボードの実装
One-way data flow
Apple Storeの模写の例
サーバーサイドウェブMVCもOne-way data flowになるかもしれない
Stimulus ControllerがReact Componentに似てくる
Zustand
Stimulusはoutletかイベントでしか通信できない
もう並列実行は怖くない コネクション枯渇解消のための実践的アプローチ
製品の検証をしてデータを取る
データのダブルチェック→時間と労力がとてもかかる
AIワークフロー
リサーチ対象商品が1ヶ月かかっても回りきらない
スレッド数が少ない
並列実行すればいいのでは?→本当に?
parallel gem
コネクションプールが枯渇
ActiveRecordのコネクションプールの仕組みについて
プロセス単位で管理
RAILS_MAX_THREADS
Sidekiqの並列処理
AIワークフローをSidekiqで動かす
Sidekiqの起動時にプロセスがコネクションを1個持つ
パラメータ設定の考え方
悲観的に見積もる
max_connectionsの設定方法
デプロイ時に2倍になりうる
Sidekiqのconcurrencyの上限
アプリケーションコードでもコネクションを意識する
ActiveRecord::Base.connection_pool.with_connection
長時間のトランザクションに気をつける
rack-lineprof
Web Componentsで実現する Hotwire とフロントエンドフレームワークの橋渡し
Web Componentsを選択した背景
新規サービスの立ち上げ、スピード感
統一されたUI
デザインシステム
Angular製
そのまま組み込めない
Rails側で管理するものを増やしたくない
Custom Element
通常のHTML要素と同様に扱えるのでHotwire上で扱える
Railsと疎結合
段階的な導入
AngularのUIコンポーネントをWeb Componentsに変換
コンポーネント内にロジックがある場合は?
Web ComponentsをRailsに導入する
独立したもの
Railsからデータを連携するもの
HTML属性経由で渡す
DOMのプロパティで渡す
エスケープちゃんとやる必要がある
data attribute経由するといい
ユーザー入力を受け取るもの
ActionViewのform helperが一般的
カレンダーだけ導入するのを試みた
選択した日付がカスタムイベントとして送信されるのでinputに連携する
多くのコードを書かないといけない状態になっている
再利用する単位が間違っていた
本来はカレンダーとinput要素を1つのCustom Elementにまとめたほうが使いやすい
利用しやすい単位でCustom Elementを設計する
form helperとの連携がしやすい形にする
form helperを拡張
input要素だけcustom elementにする
5年間のFintech × Rails実践に学ぶ - 基本に忠実な運用で築く高信頼性システム
規制産業
資金移動業
PCI DSS
国際ブランド
要件から特性を導き出す
信頼性
ビジネス・プロダクトの要求によって指標は異なる
運用を通じて実現する
セキュリティ
アーキテクチャ設計
規制対象の機能を本体から分離
権限を厳しく管理
要件に特化したシステムコンポーネントを分離
モノリス+オフロード
Citadelパターン
モジュール化
Ruby built-inのmoduleを活用
concern
金融事故対策
羃等
テスト戦略
MTTR短縮
ドキュメンテーション
イベントソーシング
Feature Flags
高信頼性を築く運用
資金移動業者向けガイドライン
「資金移動業の適正かつ確実な遂行」
速やかな検知と報告、問題解決
異常検知
SLI/SLO
決済にN秒以上かかると決済失敗→ビジネス価値を毀損する
バッチ管理
ドキュメンテーション
バッチ処理時間をSLOとして設定
アラートトリアージ
旗振り
障害対応フロー整理
PagerDutyのインシデントレスポンスを参考に整理
インシデントコマンダー
Runbook
Runbook suggestion
Kamalって便利?社内プロジェクト3つをKamal + AWSで運用した体験談
Webアプリを物理でもクラウドでもどこでもデプロイできるツール
Rails 8からデフォルト
kamal setup
サーバーとdockerレジストリ
経緯
構成
CodePipelineで自動デプロイ
ECR
EC2
RDS
EC2インスタンス内に複数プロジェクトが混在
SQLiteのプロジェクトあり
ALBのヘルスチェックが失敗する問題
Railsまでリクエストが来ていない
Kamal Proxyが原因ぽい
リクエストを適切なコンテナにルーティング
ここで止まっている
Kamal Proxyはどうやってリクエストをコンテナに振り分けているのか
ホスト名をもとに決めている
ALBはプライベートIPでリクエストする
片方のアプリでプライベートIPを受け付けるようにした
複数プロジェクトのデプロイ
自動デプロイ
CodeBuild, CodeDeploy
自動SSL対応
ファイルを分ければ環境ごとにデプロイできる
ログ収集
CloudWatch Logsにログを流せる
GraphQL×Railsアプリのデータベース負荷分散 - 月間3,000万人利用サービスを無停止で
RDBにおけるread/write splitting
テレビで取り上げられる
666rps
これまでどうやって月間3000万UUをさばいてきたのか
permalinkにはCDNキャッシュを効かせていた
トップページにはキャッシュを効かせていなかった
read/write splitting
主なアプローチ
ミドルウェア
rack middlewareではない
アプリケーションサーバーとDBの間のプロキシ
アプリケーションで分岐
Railsの機能で実現できる
モデル単位での切り替えも可能
Railsのreader/writerロールの自動切り替え
HTTPメソッドで分岐
GET,HEADはreader
POST,DELETE,PUT,PATCHはwriter
技術選定のポイント
技術に関すること
組織に関すること
アプリケーションでr/w splittingすることにした
GraphQLにおけるr/w splittingの困りごと
HTTP POSTでリクエストする
query内で更新しているかもしれない
query内でfind_or_create! している事例
CQRSパターン
graphql-rubyのtracing
フックポイントでqueryのみを実行するならreaderに接続するようにする
ホワイトリスト式のread/write splitting
ActiveSupport Instrumentation
更新SQLを正規表現で探して見つかったら例外
影響箇所をなるべく小さくしてリリースする
スキーマ単位で
管理画面から
避けられないI/O待ちに対処する: RailsアプリにおけるSSEとAsync gemの活用
Server-Sent Events
長時間かかる処理の進捗表示・完了通知
I/O wait
呼び出し先を最適化するのは難しいがユーザー体験は向上したい
RailsでSSE Responseを返却するには
背後では
HTML Standard
WebSocketに比べてシンプル
text/event-stream
Last-Event-ID
SSEと非同期処理
async gem
ThreadやFiberとの比較
ThreadはGVLの影響を受ける
Fiber
ブロッキング処理が入ると全体をブロックする
Async::Queueと相性がよさそう
イテレータ的に扱う
同時実行数制限
BarrierとSemaphoreを組み合わせる
タイムアウト
ActiveRecordのコネクションをborrowすると単一ThreadのコネクションをFiber横断で使い回してしまう
スレッドプールとコネクションプールを併用
うまみが半減
ブロッカー
レスポンスヘッダ
Cache-Control: no-store をつける (HTML標準)
Cache-Control: no-cache をつける (MDN)
こっちを採用
Cache-COntrol: no-transform をつけるとよさそう
リバースプロキシのバッファリング抑制
X-Accel-Buffering: no
SSEとそれ以外でcontroller classを分ける
committee gemのResponse Body Validation Middlewareを使うと刺さる
ミドルウェアでバッファしてしまう
クライアント側でも同様のことが起こりうる
OpenAPIとSSEの相性があまりよくない
トラブルシューティング
リクエストヘッダ・レスポンスヘッダを真っ先に見る
経路すべてをチェックする
ALBは原因にならないことが多い
chunk sizeで切られる
tcpdump
TLSに阻まれる
ブロッカーを取り除く行い
使ってるライブラリにコントリビュート
OpenAPI 3.2からSequential Media Typesが導入される
便利にしていく取り組み
async gemのunhandledな例外をハンドルする
rspec matcher