- Ruby / Rails関連
週刊Railsウォッチ: Ruby 3.2.0devにRust版YJITがマージ、Docker Compose V2ほか(20220511後編)
こんにちは、hachi8833です。
🔗Ruby
🔗 Rust版YJITがRuby 3.2.0devにマージ
つっつきボイス:「YJITがRustで?!」「Shopifyがまたすごいことをやった」「差分もすごいボリューム」「今年最大級の成果が4月に出るとは」
「ちょうど@koicさんもRust YJITをビルドしてみた記事を公開しましたね↓」「早い!」「同記事によると、Ruby 3.2.0devでYJITをRustでコンパイルするには、Rustの処理系を用意したうえで--enable-yjit=dev
オプションを指定してdev/debugモードでビルドする必要があるそうです」「--enable-yjit
だとreleaseモードでCRust版YJITをビルド、無指定だとYJITなし」
Ruby 3.2.0dev にマージされた Rust YJIT をビルドする - koicの日記https://t.co/7Iwk0TPH0s
— Koichi ITO (@koic) April 28, 2022
参考: Hello, Cargo! - The Rust Programming Language 日本語版
参考: Getting started - Rust Programming Language
「YJITをC言語で書いているうちは今後のメンテナンスや拡張が相当難しくなることは想像がつくので、Shopifyが今のうちにYJITをRustに移行して技術的負債を返済しておきたいと考えるのは理解できる」「ちなみに早くもRust YJITの勉強会が行われたそうです↓」
Rustで書き直されたYJITを読むhttps://t.co/G9WX5ROQ92
— monochrome (@s_isshiki1969) April 24, 2022
なお、上のスライド冒頭に記載されているhttps://github.com/Shopify/ruby/tree/rust-yjit-upstreamingは現在削除されています。
🔗 RubyのカスタムRange(Ruby Weeklyより)
つっつきボイス:「thoughtbotの記事です」「RubyのカスタムRangeとは?」「以下のようにMonth
クラスに<=>
やsucc
を実装する↓: こういうカスタマイズはときどき使うことがあります」
# 同記事より
class Month
# 他のメソッド
def <=>(other)
months <=> other.months
end
def succ
self.class.new(months.succ)
end
end
「Rangeオブジェクトでは少なくとも<=>
とsucc
を使うので、Month
をRangeの..
演算子で使うときはこれらを実装することになります」「rangeableとあるのはそういうことなんですね」
# 同記事より
Month.from_parts(2021, 10)..Month.from_parts(2022, 3)
# => [October 2021, November 2021, December 2021, January 2022, February 2022, March 2022]
参考: class Range
(Ruby 3.1 リファレンスマニュアル)
「これと似たような話はEnumerable
にもありますね: カスタムクラスでEnumerable
をインクルードしてeach
を実装すればenumerable機能が使えるようになる」「そうそう」
参考: module Enumerable
(Ruby 3.1 リファレンスマニュアル)
🔗 Ruby 3.1に導入された可変長アロケーション(Ruby Weeklyより)
- 元記事: How does Ruby manage memory? | Saeloun Blog
- 元記事: Ruby 3.1 introduces Variable Width Allocation for Strings | Saeloun Blog
つっつきボイス:「可変長アロケーションの話題は昨年Ruby 3.1が出る前にもしましたね(ウォッチ20210804)」「サイズプールのスロットサイズが必要に応じて40/80/160/320バイトみたいに可変になるらしい↓」「データベースのメモリ管理でもこんな感じのアロケーション戦略が使われていたりしますね」
🔗 Rubyの遅延評価
つっつきボイス:「Rubyでlazy
を付けるとどうなるかという解説のようですね」
# 同記事より
data = ["one", "two", "three"]
data2 = ["four", "five", "six"]
pipeline = data
.lazy
.map { |item| puts "item: #{item}"; item.reverse }
.take_while { |item| puts "item: #{item}"; item.length < 6 }
.zip(data2)
p pipeline.class
p pipeline.to_a
参考: class Enumerator::Lazy
(Ruby 3.1 リファレンスマニュアル)
「lazy
はパフォーマンス向上というよりは、早期復帰させたいときに使うことが多いかな」「ふむふむ」「重い処理にlazy
を付けただけで速くなるというわけではありませんが、遅延させた重たい処理を結果的に実行せずに済んだ場合は確実に速くなる」「記事でも、lazy
する処理を早期復帰させると速くなる例がありますね↓」
# 同記事より抜粋
$x.lazy.map { _1.reverse }.take(50_000).to_a # 遅い
$x.lazy.map { _1.reverse }.first(5) # 速い
🔗 その他Ruby
昨日書いたQiita記事です。初心者の人はこういうコードを書いていても自分ではなかなか気づけないので、熟練者にコードレビューしてもらうのがお勧めです。
Rubyではメソッド名と同じ名前のローカル変数を宣言しないようにしよう https://t.co/mh1snG8V8I
— Junichi Ito (伊藤淳一) (@jnchito) April 24, 2022
つっつきボイス:「@jnchitoさんのRuby/Rails記事は本当に多いですね」「コードレビューして気になった部分を記事にすることに決めているのかなと想像してみました」「自分たちもコードレビューでこうした点を指摘したりしますけど、毎回ここまで丁寧に書いているのは頭が下がる」「優しさを感じますね」
🔗DB
🔗 RailsのテストでPostgreSQLの時刻をフェイクする(Ruby Weeklyより)
つっつきボイス:「timecop gem↓やRailsのtravel_to
とかでやるような時刻がらみのテストで、RDBMS依存の時刻処理も含めてフェイクしたいということかな」
「SQLのnow()
だとそういうテストができないので、nowish()
というカスタム関数をPostgreSQLに追加してやってるんですね↓」「なるほどたしかに」「nowish()
、わかるけどすごい名前」
# 同記事より
class CreateNowishFunction < ActiveRecord::Migration[7.0]
def up
# Written to mimic the shape of `pg_catalog.now()':
#
# CREATE OR REPLACE FUNCTION pg_catalog.now()
# RETURNS timestamp with time zone
# LANGUAGE internal
# STABLE PARALLEL SAFE STRICT
# AS $function$now$function$
#
execute <<~SQL
CREATE OR REPLACE FUNCTION public.nowish()
RETURNS timestamp with time zone
AS
$$
BEGIN
RETURN pg_catalog.now() + (select global_time_offset_seconds
from public.system_configurations
limit 1
) * interval '1 second';
END;
$$
LANGUAGE plpgsql STABLE PARALLEL SAFE STRICT;
SQL
end
def down
execute "drop function public.nowish()"
end
end
「記事ではこのnowish()
を本番で使うとパフォーマンスが悪化するので通常のnow()
を使うようにとも書かれてる」「nowish()
の中でトランザクション使ってるのでたしかに遅くなりそう」
「記事ではnowish()
をマイグレーションでスキーマに追加していますけど、デフォルトでこれが使われたらバルクINSERTですごい頻度で呼び出されてさらに遅くなるかも」「もしかすると自分now()
呼びまくってたかも😅」「now()
なら高速なので問題ないと思いますよ: このnowish()
みたいに遅い処理がデフォルトで呼ばれないようにするのが大事」「なるほど」
「テストで時間をフェイクしたいニーズはたしかにありますね」「たとえば外部のAPIサービスなどが営業日や営業時間で振る舞いが変わるのをテストしたいとか」「営業時間終了後のバッチは翌日実行するみたいな要件のテストとか」「キャンペーンのような期間限定の単発機能をテストするとか」「本番はともかく、テストで時間をフェイクするのはかなり大変」「システム時刻をわざと数日進めたテストサーバーも用意して、時間がらみのバグを事前にキャッチするようにしたこともあります」「そんな方法もあるんですか」「でもこれをやると以後のメンテがつらくなるんですよ」
「そういうときに記事でやっているTravelsInTime
みたいなものが欲しくなるんでしょうね↓」「global_time_offset_seconds
という名前からやりたいことが伝わってくる」
# 同記事より
class TravelsInTime
def call(destination_time)
Timecop.return
set_pg_time!(destination_time)
set_ruby_time!(destination_time)
end
private
def set_pg_time!(destination_time)
SystemConfiguration.instance.update!(
global_time_offset_seconds: destination_time - Time.zone.now
)
end
def set_ruby_time!(destination_time)
Timecop.travel(destination_time)
end
end
「こういう時刻のフェイクはどうしても必要なら作るけど、複雑になって理解が難しくなりがちなので、できれば作らずに済ませたいですね」「やりたくなる気持ちは痛いほどわかります」「この記事いっぱい学びあったけど、実際にやるとなると考えてしまいますね」
「この記事はデータベースレベルで時刻をフェイクしているだけなのでまだいいんですが、たとえば生成したファイルのタイムスタンプもフェイク時刻に同期させようとするとさらに大変」「あ〜そうかも」「そういうときについシステム時刻を変えたくなるんですが、実はシステム時刻を変えるとAWSのAPIアクセスに失敗するという問題がある」「それそれ!そうなんですよ」「たしか30分ぐらいだったかな?とにかく時刻が一定以上ずれると失敗します」
「一見すぐやれそうに見えるのに、今の時代だと時刻をずらすのがこんなに大変だったとは...」「時刻をずらすといろんな弊害が起きる可能性があることは知っておいて損はないと思います」
🔗クラウド/コンテナ/インフラ/Serverless
🔗 Docker Compose V2が正式リリース
つっつきボイス:「V2って既に使っていますけど、今まで正式じゃなかったんですか」「GAは今回からですね」「V1はPythonベースだったけどV2はGo言語ベースに変わった」
参考: GA版(General Availability)とは - IT用語辞典 e-Words
「GAリリースによって変わったことにひとつに、Compose V1のEOL(End Of Life)が確定したことがありますね」「いつですか?」「2023年4月からV2が必須になるので1年後です」「結構早い...」「そのときにはV1とV2を切り替えるcompose-switchも↓使えなくなります」
「今V1とV2のどちらなのかを調べるにはどうすればいいんでしたっけ?」「docker compose version
でわかりますし、そもそもV1ではハイフンありのdocker-compose
だったのがV2ではハイフン無しのdocker compose
に変わります」「う、自分V1だったか...」「docker-compose
でV2を動かす方法も一応あるので微妙なんですが、原則としてハイフン無しならV2、ハイフンありならV1を目安にしてよいと思います」「これからはハイフン無しにしないと」
参考: Docker Compose V2で変わったdocker-compose.ymlの書き方
🔗 act: GitHub Actionsをローカルで実行
act使ってるけど便利よ / acthttps://t.co/x198pS6mRA
— すろっくさん (@srockstyle) April 28, 2022
つっつきボイス:「このactは今使ってますけど、ローカルで動かして確かめてからGitHub Actionsにプッシュできるので便利ですよ」「自分がact使ってみたときは思うように動かなかったなぁ」「そうそう、最初なかなか動かないんですけど、動き出すと便利」
参考: GitHub Actionsのローカル実行ツール「act」を使う事でCI/CDコンフィグとローカルでのタスクランナーを1つにする | DevelopersIO
「おそらくGitHub Actionsを何に使うかによるんでしょうね: GitHubの環境に強く依存しているものを扱うには不利なのかも」「確かにそれはありますね」「自分のときは、GitHub Actionsの中でコンテナイメージをビルドして、AWS ECRにプッシュして、Fargateにあるタスク定義のリビジョンを更新するという複雑なことをやってて、それこそローカルで試してからにしたい内容だったんですが思うようにいかなかった」「あ、それと同じことをちょうどやろうとしてます」
参考: Amazon ECR(Docker イメージの保存と取得)| AWS
参考: Amazon ECSの タスク定義 - Amazon ECS
「GitHubランナーの環境はたしかGitHub用のUbuntuで、いろいろカスタマイズされているので、ローカルのUbuntuで動かそうとすると細かな点に違いが見つかる」「そうなんですよ、ローカルだと動くのにプッシュすると動かなかったり」「actのREADMEにもランナーごとの環境の違いが載ってますね↓」
「自分のときは諦めて直接GitHub Actionsにプッシュしてテストしましたが、もちろん簡単な内容ならactで十分ローカルテストできると思います」「そうそう」「思いつきですが、最近話題になっているDagger↓とこのactを組み合わせられるか試してみたい気もしますね」
参考: dagger.io
参考: 話題の CI/CD ツール Dagger を体験してみる | 豆蔵デベロッパーサイト
🔗言語/ツール/OS/CPU
🔗 スライド: Chromiumのアーキテクチャ
つっつきボイス:「Chromium解説スライドです」「以前のChromiumはGCで動きがカクカクすることがあった話とかセキュリティとかDOMの話など、詳しく載ってますね」「生ポインタの参照カウンタの話でObjective-Cという言語を思い出した↓」
「スムーズに動いているように見せるのに必要な最小FPS、これも有名な話ですね↓」
「Chromiumのにはharfbuzz↓というライブラリが使われているのか」「テキストシェイプライブラリって初めて聞くかも」「このharfbuzzが、フォントのリガチャや改行を制御しながら表示位置を決めているんですね」
参考: 合字 - Wikipedia -- リガチャ
「harfbuzzは、Linuxのcairoというグラフィックライブラリに含まれているPangoと似たようなことをしているみたいですね: 以前PDFにフォントを埋め込む必要があったときにこのあたりを調べたのを思い出しました」
参考: cairo - Wikipedia
参考: Pango - Wikipedia
「ところでどこかで見たスライドだと思ったら、一昨年に2020年版のスライドを見たことがありました↓」「なるほど、上のスライドはこのスライドの更新版でしたか」
参考: 電子情報学特論:Chromiumのアーキテクチャを解き明かす - Google スライド
後編は以上です。
バックナンバー(2022年度第2四半期)
週刊Railsウォッチ:(20220510前編)Active RecordにPromiseと非同期集計メソッドがマージ、climate_control gemほか
- 20220419後編 RubyのGCコンパクション改修、jemalloc、ReDoSの自動検出修正ほか
- 20220418前編 RailsConf 2022が5月17〜19日開催、認可機能解説記事ほか
- 20220412後編 HashieでRubyのハッシュを強化、最近のRubyコア解説記事ほ
- 20220411前編 Turbo Railsチュートリアル、Active Recordの「Leaky Abstraction」を削減ほか
- 20220406後編 RBS関連記事、Ruby formatterプロジェクト、Google Cloud Runほか
- 20220404前編 Ruby 3.2.0 Preview 1リリース、Rails向けDocker環境ジェネレータ、scientist gemほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)