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

週刊Railsウォッチ(20201020前編)Percona Toolkitは優秀、Active Admin非公式ガイド、Railsをリアクティブにするガイドほか

こんにちは、hachi8833です。

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

※今回録音に失敗したため、つっつきボイスがいつもより短めとなっております🙇。

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

今回の更新は少なめで、Changelogは更新されていませんでした。

render_inに応答するオブジェクトのレンダリングでレイアウトもサポート

#37919で、コントローラ内でrender_inに応答するオブジェクトのレンダリングサポートが追加されたが、この実装ではレイアウトがサポートされていなかった。
今回の変更では#37919の実装を更新してActionView::Templateと統一し、レイアウトを利用できるようにした。
また、ViewComponentがRailsの一部でないという点を明示するため、「component」という用語を「renderable」にリネームした。
#39869より大意


つっつきボイス:「render_inか」「これを入れた背景が知りたい」「例のViewComponentからつながる流れかなと思ったら#37919で以下へのリンクを貼っているのでそのようですね↓」「ViewComponentはウォッチでも話題にしましたね(ウォッチ20200330)」

ViewComponentは、React Componentsにインスパイアされたビューレンダリングクラスであり、データを受け取って出力安全なHTMLを返す。PresenterやDecoratorやViewModelパターンの進化系と思えばよい。
コンポーネントは、ビューのコードが再利用されるほとんどの場面で効果を発揮し、直接テストできるというメリットもある。
週刊Railsウォッチ20200330より再録

github/view_component - GitHub

「この改修はどんなときにいいんでしょうか?」「新しいViewComponentを通したレンダリングを考えると、複数のViewComponentを状況に応じて切り替えるのに使いたいんだと思います」

DatabaseConfigurations#find_db_configを安定ソートにした

参考: 安定ソート - Wikipedia


つっつきボイス:「たしかにconfigの項目順序が変わると思わぬバグやエラーの温床になるかもしれないので怖い」「安定ソートにRubyのwith_indexを使っていますね↓」

# activerecord/lib/active_record/database_configurations.rb#L81
    def find_db_config(env)
      configurations
-       .sort_by { |db_config| db_config.for_current_env? ? 0 : 1 }
+       .sort_by.with_index { |db_config, i| db_config.for_current_env? ? [0, i] : [1, i] }
        .find do |db_config|
          db_config.env_name == env.to_s ||
            (db_config.for_current_env? && db_config.name == env.to_s)
        end
    end

ActiveSupport::Cacheでカスタムキャッシュエンコーダーを設定できるようになった

Rails api: ActiveSupport::Cache

背景

自分たちは現在https://github.com/Shopify/memcached_storeを使っているが、中期的にdalliベースがデフォルトのMemCacheStoreに移行したいと思っている。カスタムキャッシュエンコーダーを設定できる機能が現在ないので欲しい。
何らかの形でMemCacheStore内の圧縮を無効にしてからdalliクライアントにカスタムエンコーダーを渡せば一応やれるが非常にアドホックだし、RedisStoreを使おうとするとどちらもできない。
このあたりを調べてみていくつかわかったことがある:

  • 最小エントリのオーバーヘッドMarshal.dump(ActiveSupport::Cache::Entry.new("")).bytesize # => 107の無駄が大きい。これを大きく削減して圧縮フラグをたとえば1バイトにまで減らせる。
  • MemCacheStoreをデフォルト設定で使うと、ZLib.deflate(Marshal.Dump(Entry.new(ZLib.deflate(Marshal.dump(actual_value)))))と値が何度もシリアライズおよび圧縮され、無駄な計算が行われている。
  • issue #9494を見つけて読んでみたところ数年前にも同様の改良が試みられていたらしいが、前方および後方互換性維持のため取り消されていた。

提案

このプルリクは最初の一歩にすぎない。自分の関心は用途に特化した極めて高効率のエンコーダーを利用できるようにすることだが、Entryエンコーディングメカニズムが置き換え可能になればより新しく効率の高いシリアライゼーション形式をすべてのユーザー向けにロールアウトしやすくなるだろうと思った。
以下のような3つのステップになるだろう。

  • 次バージョンのリリースでは新旧フォーマットをどちらも読める(が旧フォーマットで書き込む)デフォルトのコーダーをリリースし、互換性を維持する。
  • 新しく作成されるアプリは最適化済みの新しいフォーマットでのみ読み書きできるよう設定される。
  • キャッシュをフラッシュせずにアプリを移行したい場合は、第三の設定を用いて旧フォーマットで読み、新フォーマットで書き込む。
    同PRより大意

つっつきボイス:「Shopifyからのプルリクです」「エンコーダーを差し替えできるようにすることでキャッシュを飛ばさずにCache Storeのエンジンを切り替えたいということでしょうね: キャッシュヒット率の高いアプリだったり、事前に生成したキャッシュを積極的に使うようなアプリの場合、キャッシュをいきなり全飛ばしするとサイトが落ちる可能性があるので」「なるほど」

参考: 3 キャッシュのキー -- Rails のキャッシュ機構 - Railsガイド

「上の3つのステップで互換性を維持しつつキャッシュエンコーダーを段階的に移行すると」「そういえばRailsの今のMemCacheStoreはデフォルトでdalliベースになっていますね」

参考: 8.7 Action Pack -- Rails アップグレードガイド - Railsガイド

Rails 4.0のデフォルトのmemcachedクライアントがmemcache-clientからdalliに変更されました。アップグレードするには、単にgem 'dalli'をGemfileに追加します。
Railsガイドより

petergoldstein/dalli - GitHub

WIP: 高粒度なロールやシャーディングスワップを実装

これは6.1.0マイルストーン経由で見つけました。


つっつきボイス:「まだマージされていませんけど」「以下の上の図が従来で、下の図が提案だそうです」「ソシャゲのようにシャーディングが非常にうまく機能する世界だと上の図のような構成がよくありますね」


同PRより

「プルリクの文章長いですね...」「マルチデータベースのシャーディングはまだproductionでやったことがないのでちゃんと読まないといけないと思いますが、見た感じでは従来だとreadとwriteでConnectionHandlerが別だったのを、ひとつのConnectionHandlerにしてその中でreadとwriteを切り替えるということらしい」「readとwriteが一つのtransactionなり一連のフローの中で使われるときに、異なるConnectionHandlerに分かれているよりも一つのConnectionHandlerにまとまっている方が扱いやすいということでしょうね」

「ConnectionHandlerが一つにまとまると、まさにこのサンプルコードのようにコンテキストを共有できますね↓」「role: :writing, shard: :onerole: :writing, shard: :twoを指定してる」「こうやって入れ子にしたときにたとえばDog.firstが1回目と2回目で読み出し元が変わるのか」

# 同PRより
ActiveRecord::Base.connected_to(role: :reading) do
  User.first # reads from default replica
  Dog.first  # reads from default replica

  AnimalsRecord.connected_to(role: :writing, shard: :one) do
    User.first # reads from default replica
    Dog.first  # reads from shard one primary
  end

  User.first # reads from default replica
  Dog.first  # reads from default replica

  ApplicationRecord.connected_to(role: :writing, shard: :two) do
    User.first # reads from shard two primary
    Dog.first  # reads from default replica
  end
end

Rails

Active Admin非公式ガイド(Ruby Weeklyより)


つっつきボイス:「元記事冒頭に、Active Adminが合うのは15分で構築するブログぐらいなのはわかっているけど他にも機能があるよと書かれていました」「Active Adminは、もし入れるなら極力カスタマイズしないて欲しいですね、せいぜいカラムのオンオフぐらいに留める感じで」

activeadmin/activeadmin - GitHub

「Active Adminは短期間で開発する小規模プロジェクトなら向いていると思いますが、カスタマイズすればするほどconcernsやら何やらで深みにハマっていく傾向があるんですよね」「ちなみにこの間管理画面が必要になったときにActive Admin入れました」「最近は画面づくりをフロントエンド側でやることが増えてきているので、Railsは基本的にAPIに徹するのがいいのかもしれないという気持ちがちょっとありますね」「それわかります」「Active Adminはそういう部分を理解したうえで導入するのが大事」

参考: 2020 Ruby on Rails Community Survey Results | 2020 Ruby on Rails Community Survey Results

「なお元記事は上のアンケート↑のfrustrated gem top 10にActive Adminが入っていたのをきっかけに書いたそうです↓」「同じtop 10にRailsも入ってる😆」「RuboCopが入っているのはちょっとわかる気がする」「RuboCopは素の設定が厳し目なので、ある程度設定を案件に合わせてカスタマイズしてから使うのが常道ですね」

rubocop-hq/rubocop - GitHub

Railsをリアクティブにするガイド(Ruby Weeklyより)


つっつきボイス:「この記事ではRailsアプリをインタラクティブにするための最新情報をまとめてくれています」「少人数で開発できるなら、記事にもあるAction CableとTurbolinksとStimulusというRails wayでやるのが定番でしょうね」「なおReactive Railsという言葉はこの記事で独自にそう呼んでいるみたいです」「フロントエンド界隈の技術スタックをなるべく使わずに、Rails Wayを主体としたフロントエンド実装でやりたいのであればこの記事を参考にするとよさそう👍」

Departure: MySQLをダウンタイムゼロでマイグレーション(Ruby Weeklyより)

departurerb/departure - GitHub


つっつきボイス:「このDepartureというgemは以前Percona Migratorという名前だったそうです」「この中身はPercona Toolkitのpt-online-schema-changeを呼び出しているのね」

# 同リポジトリより
$ PERCONA_ARGS='--chunk-time=1 --critical-load Threads_running=55' bundle exec rake db:migrate:up VERSION=xxx

参考: pt-online-schema-change

Percona Toolkitは優秀

percona/percona-toolkit - GitHub

「Percona Toolkitは昔からありますけど、とてもいいですよ」「え、そんなにいいんですか?」「自分はMySQLのチューニングするときにまずこれを入れますし、これなしではチューニングする気にならないほどです」「うう、MySQL派なのにPercona Toolkit知らなかったとは😅」

参考: MySQL Tools and Management Software to Perform System Tasks by Percona

「たとえば以下の記事↓でも紹介されていますけど、Percona Toolkitを使うと普通なら別々の項目になってしまうN+1クエリのログをグループ化して見つけやすくできます」「おぉ〜!」「MySQLデフォルトのslow_logは『1回の実行で遅いクエリ』は取れるけど『アプリケーションの1リクエスト中で何度も呼び出されてその合計時間が遅いクエリ』は取れません: pt-query-digestは、そういったN+1的な単体ではそこまで遅くないけど大量に呼び出されると全体のパフォーマンスに影響を与えるようなクエリを特定することができます」

参考: スローログの集計に便利な「pt-query-digest」を使ってみよう | Think IT(シンクイット)

後でPercona Toolkitは以前Maatkitという名前だったと教わりました。

参考: Maatkit - Wikipedia


以下はつっつき後に見つけた昨年のPercona公式ツイートです。

その他Rails


つっつきボイス:「RubyKaigi Takeout 2020Kaigi on Railsの動画が順次公開されててYouTubeに通知が表示されてますね」「運営の仕事は大変」「皆さまお疲れさまです!」

YouTubeチャンネル: (6) RubyKaigi - YouTube

2020/10/21: ご指摘をいただき上記誤記を訂正いたしました。両イベント関係者にお詫び申し上げます🙇🙇。


つっつきボイス:「上のアジャイルソフトウェア開発宣言↑をこのツイートで初めて知りました」「DHHはアジャイルの具体的なプラクティスよりマニフェストから受けた影響の方が大きかったんですね」「DHHがこう言うのは何だかとてもわかる: こういう技術の思想的哲学的な部分って、最初のうちはなかなかわからないけど、読んだ後に何年か経ってからある日突然『そうか!』って腑に落ちたりしますよね」「それたしかに!」「先週話題になって買ったClean Agileも自分にとってそういう本かもしれないと思いました↓」

参考: Clean Agile 基本に立ち戻れ【委託】 - 達人出版会

「でもその本を他の人にすすめても、同じポイントで感動してもらえるとは限らないということもよくあります」「まさに」「経験上そういう本が相手に刺さることってなかなかない気がしました」「本って本当に出会いとしか言いようがないですね」「出会わなかったらそれまでですよね」「そういう出会いの機会を増やすという意味でも多読や速読は有用だなって思いました」「自分に合う本に出会うまで乱読して数をこなすのも一つの方法かなと思います」


以下はつっつき後に見つけたツィートです。


前編は以上です。

バックナンバー(2020年度第4四半期)

週刊Railsウォッチ(20201013後編)ruby-type-profilerがtypeprofにリネーム、AWS API Gatewayの実行ログは便利、M5Stackほか

今週の主なニュースソース

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

Rails公式ニュース

Ruby Weekly


CONTACT

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