- Ruby / Rails関連
週刊Railsウォッチ: Rubyの可変長アロケーションプロジェクト、サーキットブレーカーgem、EC2-Classicが終了へほか(20210804後編)
こんにちは、hachi8833です。
🔗Ruby
🔗 Rubyの可変長アロケーションプロジェクトがフェーズIIに
Really excited to reveal the next phase of the Variable Width Allocation project. We've made it a lot faster than our original implementation that we merged a few weeks ago. Shoutout to @eightbitraptor and @tenderlove for their significant contributions. https://t.co/WXIYuFIcEJ
— Peter Zhu (@peterzhu2118) July 26, 2021
つっつきボイス:「Rubyのアロケーターをリニューアルして高速化するプロジェクトが進行中なんですね」「スゴい」「これはRubyKaigiに向いていそうなお題」「そのうち解説記事が出るといいですね」
サマリー:
- 現在はアロケーション高速化のために、スロットサイズの異なるページをプールで使っている。要求されたサイズを収納できる最小のスロットサイズでオブジェクトが割り当てられる。これによって、フリーリストベースのアロケーションアルゴリズムに立ち返り、アロケーションのパフォーマンスが大幅に向上した。
- サイズプールの成長パターンに合わせてヒープの成長アルゴリズムが変更された。ヒープのアロケーションと解放を繰り返すと「成長ヒープ」とみなされ、サイズの安定したヒープと異なる扱いになる。
同issueより
🔗 time_up
gemでRubyコードを計測する(Ruby Weeklyより)
つっつきボイス:「time_upというgemを作って測定してみた記事だそうです」
「測定したいコードにTimeUp.start
でタイマー名を:processing
や:querying
のように複数指定できる」
# 同記事より
transactions = TimeUp.start(:querying) {
Transaction.where(user: @user)
}
process_timer = TimeUp.start(:processing)
categorized_transactions = group_by_category(transactions)
monthly_spending = arrange_by_month(categorized_transactions)
process_timer.stop
payments = TimeUp.start(:querying) {
PaymentFinder.find(@user, monthly_spending)
}
render json: TimeUp.start(:presenting) {
present_summary(
spending: monthly_spending,
payments: payments
)
}
「するとTimeUp.timer
でタイマー名ごとのカウントや最大最小平均が取れるようになる」
# 同記事より
> TimeUp.elapsed(:presenting)
=> 1.0050820000469685
> query_timer = TimeUp.timer(:querying)
> query_timer.count
=> 2
> query_timer.timings
=> [0.5051469999598339, 0.8050760000478476]
> query_timer.min
=> 0.5051469999598339
> query_timer.max
=> 0.8050760000478476
> query_timer.mean
=> 0.6551115000038408
「TimeUp.all_stats
すると全タイマー名の測定結果を取れる」
# 同記事より
> TimeUp.total_elapsed
=> 2.822688000043854
> TimeUp.all_stats
=> {
:querying=>{:elapsed=>1.3102230000076815, :count=>2, :min=>0.5051469999598339, :max=>0.8050760000478476, :mean=>0.6551115000038408},
:processing=>{:elapsed=>0.5073829999892041, :count=>1, :min=>0.5073829999892041, :max=>0.5073829999892041, :mean=>0.5073829999892041},
:presenting=>{:elapsed=>1.0050820000469685, :count=>1, :min=>1.0050820000469685, :max=>1.0050820000469685, :mean=>1.0050820000469685}
}
「TimeUp.print_detailed_summary
で整形できる」
# 同記事より
> TimeUp.print_detailed_summary
=================================================================================
Name | Elapsed | Count | Min | Max | Mean | Median | 95th %
---------------------------------------------------------------------------------
:querying | 1.31028 | 2 | 0.50520 | 0.80507 | 0.65514 | 0.65514 | 0.78221
:processing | 0.50314 | 1 | 0.50314 | 0.50314 | 0.50314 | 0.50314 | 0.50314
:presenting | 1.00508 | 1 | 1.00508 | 1.00508 | 1.00508 | 1.00508 | 1.00508
「測定ポイントごとのデータを一括で管理しつつ簡単な集計が行えるようですね: 記事タイトルにはベンチマークとあるけど計測という方が近いかな」「なるほど」「通常だと自分でグローバルな場所に値を保存することになるでしょうけど、time_up gemを使うと一括で管理できる: 知っていたら使うかも👍」
🔗 メモリ上のRuby文字列を見る
Link: メモリの上に置かれているRubyの文字列を覗く - ローファイ日記 https://t.co/wTKyeM8p7i
— Yukihiro Matsumoto (@yukihiro_matz) July 29, 2021
つっつきボイス:「お〜、 /procファイルシステムからプロセスの生ユーザーメモリ空間を解析して、その中からRString
として使われている部分を参照して復元したんですね」「これは面白い❤️」「RString
なら比較的やりやすいでしょうね: これがRObject
だと難易度が上がりそう」「こういう作業は理解が深まりますね: Ruby内部でどのようにメモリをマッピングして使っているかを学ぶのによさそう👍」
🔗 サーキットブレーカーgem 2種類
つっつきボイス:「Jeremy Evansさんの近著『Polished Ruby Programming』↓を読んでいて、サーキットブレーカーはgemでやる方が楽という記述があったので探してみました」
"Polished Ruby Programming" is now available for purchase! The book teaches design principles, best practices, and appropriate trade-offs to the intermediate Ruby programmer. I look forward to seeing what people think about the book. https://t.co/wFRn4yHbVC @PacktPub
— Jeremy Evans (@jeremyevans0) July 9, 2021
「今さらですけどサーキットブレーカーって何でしょう?」「サーキットブレーカーはKubernetesやマイクロサービスなどでサービス同士がチェインする部分で使われるパターンですね」「お〜」「以下のIstioドキュメントに簡単な説明があった↓」
サーキットブレーキングは、レジリエントな(回復力のある)マイクロサービスアプリケーション構築で重要なパターンです。サーキットブレーキングを使うと、障害やレイテンシの急増、その他のネットワークの特異性による望ましくない影響を制限するアプリケーションを書けます。
istio.ioより
「たとえば、あるサービスがリクエストを返すために別のサービスに問い合わせる必要があってみたいなチェインが繰り返されると、途中で1箇所でも障害が起きたときにさんざん待たされた末に全部コケたりしますけど、そういうのをうまく扱うためにサーキットブレーカーを挟む感じですか?」「だいたいそんな感じ: タイムアウトなども細かく管理できます」「お〜」「サーキットブレーカーはKubernetesやIstioのようなサービスには必ずあって、マイクロサービスのコントロールプレーンの話題にもよく登場します」「なるほど」
参考: コントロールプレーン | TED用語集 | 東京エレクトロンデバイス
参考: 分散サービス環境へのCircuit Breakerの適用 - LINE ENGINEERING
「サーキットブレーカーでは、たとえばチェインしているサービスの手前の段の処理が軽くて奥の段の処理が重いとすると、軽い手前のサービスがリクエストをたくさん投げると奥の処理が詰まってしまうので、奥の段の処理が詰まっていることをサーキットブレーカーが何らかの形で検知すると、手前の段で早期に失敗させる、というような処理を行います」「なるほどそういう感じですか」
「でgemの方を見てみると、Circuitboxは文字どおり失敗時に転送している↓」
Circuitbox.circuit(:your_service, exceptions: [Net::ReadTimeout]) do
Net::HTTP.get URI('http://example.com/api/messages')
end
「以下のコンフィグでタイムアウトやスリープやスレッショルドなどを指定できる↓」
# github.com/yammer/circuitboxより
class ExampleServiceClient
def circuit
Circuitbox.circuit(:your_service, {
# exceptions circuitbox tracks for counting failures (required)
exceptions: [YourCustomException],
# seconds the circuit stays open once it has passed the error threshold
sleep_window: 300,
# length of interval (in seconds) over which it calculates the error rate
time_window: 60,
# number of requests within `time_window` seconds before it calculates error rates (checked on failures)
volume_threshold: 10,
# the store you want to use to save the circuit state so it can be
# tracked, this needs to be Moneta compatible, and support increment
# this overrides what is set in the global configuration
cache: Circuitbox::MemoryStore.new,
# exceeding this rate will open the circuit (checked on failures)
error_threshold: 50,
# Logger to use
# This overrides what is set in the global configuration
logger: Logger.new(STDOUT),
# Customized notifier
# overrides the default
# this overrides what is set in the global configuration
notifier: Notifier.new
})
end
end
「もうひとつのcircuit_breaker gemの方はinclude
して使う感じで、できることは上と似ているかな」「なるほど」
# github.com/wsargent/circuit_breakerより
require 'circuit_breaker'
class TestService
include CircuitBreaker
def call_remote_service() ...
circuit_method :call_remote_service
# Optional
circuit_handler do |handler|
handler.logger = Logger.new(STDOUT)
handler.failure_threshold = 5
handler.failure_timeout = 5
handler.invocation_timeout = 10
handler.excluded_exceptions = [NotConsideredFailureException]
end
# Optional
circuit_handler_class MyCustomCircuitHandler
end
「サーキットブレーカーをちゃんと使ったことはまだありませんが、サービスが大規模に連携するようになるとこういうgemが欲しくなるのかも」
🔗 サーキットブレーカー自作は避けたい
「そういえばJeremy Evansさんが『サーキットブレーカーを自分で作るとつらいよ』みたいなことを書いていました」「はい、この種の機能を自作するのは避けた方がよいと思います」
「たとえば自分の実装で排他制御周りをしくじってリソースが永遠に開放されなくなってしまうバグを踏むと、サーキットブレーカーが誤動作してすべてのリクエストが止まってしまったりします」「ありゃ〜それはつらい」「かといって逆に排他制御の設定を厳しくしすぎると、今度はサーキットブレーカーがボトルネックになってしまったりします」「う〜む」「どちらもサーキットブレーカー的なものを自分でちょろっと作ったときにありがちなバグなので、自前実装は避けたい」
参考: 排他制御 - Wikipedia
Azureのドキュメントによると、以下の『Release It!』という書籍でサーキットブレーカーパターンが広まったそうです。
🔗 その他Ruby
GSoC プロジェクトの一部で、でデバッガのテストを3倍高速化してくれた うれしい。
— _ko1 (@_ko1) July 29, 2021
つっつきボイス:「GSoCって何だろうと思ったらGoogle Summer of Codeのようですね」「オープンソースソフトウェアをテーマに毎年開催されている学生向けのイベント」「そういえばGoogle Summer of CodeでRubyのプロジェクトも見たことあります↓」
参考: Ruby | Google Summer of Code
参考: Google Summer of Code - Wikipedia
🔗DB
🔗 rails-pg-extras(Ruby Weeklyより)
つっつきボイス:「RailsのPostgreSQL関連機能を拡張するのかな」「heroku-pg-extrasをRailsに移植したgemのようです↓」「Herokuと無関係に使えそうですね」
「何ができるんでしょう?」「MySQLで言うINFORMATION_SCHEMA的な情報をRubyオブジェクトとして取得できるみたい↓」「お〜、Railsコンソールでも取れるのかな?」「サンプル出力を見るとRubyのコードからメトリクス情報がオブジェクトとして取得できているようなので、できるということでしょうね」
# 同リポジトリより
RailsPGExtras.index_cache_hit
$ rake pg_extras:index_cache_hit
| name | buffer_hits | block_reads | total_read | ratio |
+-----------------------+-------------+-------------+------------+-------------------+
| teams | 187665 | 109 | 187774 | 0.999419514948821 |
| subscriptions | 5160 | 6 | 5166 | 0.99883855981417 |
| plans | 5718 | 9 | 5727 | 0.998428496595076 |
(truncated results for brevity)
「この種のデータベースメトリクスをRubyのコードから参照したいと思ったことはなかったけど、こういうふうにRubyのコンテキストで使えるなら、たとえばメトリクスをrakeタスクで処理したりRailsコンソールでデバッグしながら見たりできそう」「おぉ」「他にも、最近だとWebアプリのステータスを返す専用のURLを用意して各種情報を見られるようにすることがありますけど、そこにこういう情報を追加してメトリクスを継続的に収集できるようにするといった使い方も考えられますね👍」
🔗クラウド/コンテナ/インフラ/Serverless
🔗 EC2-Classicが終了に向かう
むしろこれまだサポート続いとったんかいっていう / EC2-Classic is Retiring - Here's How to Prepare https://t.co/FjhPWF3iBt @awscloudより
— すろっくさん (@srockstyle) July 28, 2021
つっつきボイス:「お〜我のツイート、morimorihogeさんのツイートを見て反応したヤツです」「EC2-Classicを使っていた人はただでさえ相当少数派だと思うので、EC2-Classic終了の影響をすぐに理解できる人が果たしてどれだけいるのかなと思ったりしました」「自分はEC2-Classicから移行した経験あるのでわかりますけど、EC2-Classicって随分昔の話ですよね」「そうそう」
「自分もツイートしましたけど、EC2-Classicが終わるとPVインスタンスが動かなくなるのが問題」「あ〜わかります」「PVがなくなるとHVMだけになるので、PV->HVMの移行はそれなりに大変そう」
EC2 Classicサポート終了かー。これの何がヤバいって、対応インスタンスタイプ的にPV系のAMIサポートが切られること(そのはず)。PV -> HVM移行は一応そこそこプラクティスがあるが、ハマるとめんどくさそう・・・ https://t.co/dUGWEXed3h
— Masato Mori (@morimorihoge) July 28, 2021
「まだPVで動いていたものが世の中にあったとは」「AWSはそう簡単に古いサービスを終了しないのがよい点ですが、長期間サポートにも限度はあるので終了は仕方がないでしょうね」「これはもう終了やむなし」
「それにPVインスタンスだった頃のAWSの常識的な運用は、現代のAWS運用から見るとかなりかけ離れているんですよ」「たしかに」「たとえば当時はRDSのようなサービスが高い割にメリットがあまり感じられなくて、コストを考えるとRDSを使わずに自分でEC2にMySQLサーバーを立てることが多かった」「そうそう、当時はよくそれでやってました」「今は?」「今はRDSを使うメリットが圧倒的に大きくなりましたね」
参考: Amazon RDS(マネージドリレーショナルデータベース)| AWS
「現代のAWSの常識で組まれたシステムなら比較的移行しやすいんですが、EC2-Classicという時点で昔の常識でシステムが組まれている可能性が高いので、PVからHVMに移行したりDBを移行したりするのは大変そうだなと思いました」
「今もEC2-Classic使ってるサービスってどのぐらいあるんだろう?」「HVMインスタンスへの移行が終わってさえいればそれほど大変ではないんですが、うんと古くからあるサービスだと移行する機会のないままEC2-Classicを使い続けているところもあるでしょうね」「つらそう...」「PV->HVM移行ではたとえばプライマリパーティションが変わるのでMBR(Master Boot Record)も変わるんですよ」「え〜!」「だからdd
コマンドでコピーして終わりというわけにいかない」
参考: マスターブートレコード - Wikipedia
参考: dd (UNIX) - Wikipedia
「調べればPV->HVM移行のノウハウは見つかりますけど、何しろ古いのでやってみないとわからない部分も多いし、そもそもEC2-Classicをやっていた人数が少ない」「EC2-Classic自体を知らない人も多そうですよね」「Classic-ELBのことだと思う人がいたりして」「ありそう」「Classic-ELBは今も問題なく使えますけど、EC2-Classicの古さはレベルが違う」
参考: What is a Classic Load Balancer? - Elastic Load Balancing
「EC2-Classic終了は大きいニュースなんですね」「古くからやっているところにとってはかなり大きいニュースだと思います」「リリース記事を見ると2021/10/30には動いていないEC2-Classicのインスタンスが起動できなくなって、翌年の2022/08/15にはすべて終了するとありますね」「割とすぐなのか〜」「AWSの終了スケジュールは少なくともまず前倒しになることはないと思います: 逆に何やかやで延期する可能性の方があるかも😆」
「EC2-Classicがサービスインしたのが2006年か: 当時はCore 2ぐらいの時代だから、サービス止めたいのもわかる」「あの当時はAWSコンソールもなければシンガポールリージョンもなかった」「USのリージョンだけだったんですね」「自分がAWSを使い始めたのはEIPが始まった頃だから2008年ぐらいかな」
参考: Intel Core 2 - Wikipedia
参考: Elastic IP アドレス - Amazon Elastic Compute Cloud
🔗言語/ツール/OS/CPU
🔗 PHPチュートリアルのSQLインジェクション
GoogleでPHPのチュートリアルを検索すると、30件中16件がSQLインジェクションを起こすような例になっている。チュートリアルがこんな状態では到底、SQLインジェクションはなくならない。https://t.co/yqT16xuhXO
— 新山祐介 (Yusuke Shinyama) (@mootastic) July 25, 2021
つっつきボイス:「ググって見つかったPHPチュートリアルで30件中16件にSQLインジェクションが見つかったという記事でした」「mysqli_query
を直接使っている時点でヤバい」「これ直接使わないヤツですよね」
// 同記事より
// Don't do this!
mysqli_query("SELECT * FROM user WHERE id = '" . $_POST["user'] . "'");
「考えてみれば、RailsはいわゆるCGIも経験せずにいきなりWeb開発ができるわけで、むしろ特異なのかも」「まあたしかに」「他の言語だと、昔ながらのCGIから始めてHTML埋め込みでWebページにhello worldや日付を表示するところから学んだものですが、Railsはrails new
から始まりますから」「まるで違いますよね」
参考: Common Gateway Interface - Wikipedia
「Railsだけやってきた人から見るとわかりにくいかもしれませんが、昔は他の言語のチュートリアルの一環としてこういうふうに生SQLを動かしてみるという教え方が普通でしたね」「そうそう」「インターネット昔話になってきた」
「脆弱性うんぬんを別にすれば、チュートリアルなどでこういうふうに1行1行何が行われているかを確かめながら学ぶことは初学者にとって有用な面もあると思うんですよ」「わかります」「業務で使わなければOK」
「逆にRailsは初学者にとって便利すぎて裏で何が行われているのかさっぱりわからないですよね」「ブレークポイント置いても追いきれない😆」「Web開発をRailsから始めた人の中にはCGI時代のWebアプリの仕組みを知らずに普通に開発している人もいると思うので、昔と今とどちらの学び方がいいのか自分には断定しきれないかなという気持ちです」
後編は以上です。
バックナンバー(2021年度第3四半期)
週刊Railsウォッチ: SorbetでRailsアプリの型シグネチャを書く、activerecord-cte gemとanycable-client gem(20210803前編)
- 20210720後編 ruby-gitでGit操作、最近のruby/debug、stdgems.org、Windows 365 Cloud PCほか
- 20210719前編 GitHubによるdisable_joins解説、MemoWise gemでメモ化、RailsのDDoS攻撃対策ほか
- 20210713後編 ruby-spacyで自然言語処理、Ruby製x86-64アセンブラ、『タイムゾーン呪いの書』ほか
- 20210712前編 AR::Relation#destroy_allがバッチ分割に変更、Active Record暗号化解説、sidekiq-unique-jobsほか
- 20210706後編 GitHub CopilotのAI補完、Pure Ruby実装のRuby JIT rhizome、PostgreSQLのPG-Strom拡張ほか
- 20210705前編 DI的な書き方が必要なとき、脆弱性学習用アプリRailsGoat、brakemanは優秀ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)