- Ruby / Rails関連
週刊Railsウォッチ(20201006後編)Rubyの`defined?`キーワード、Ractorベースのジョブスケジューラ、Caddy Webサーバーほか
こんにちは、hachi8833です。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- お気づきの点がありましたら@hachi8833までメンションをください🙇。
⚓Ruby
⚓Ractorベースのジョブスケジューラを書く(Ruby Weeklyより)
# 同記事より
pipe = Ractor.new do
loop do
Ractor.yield(Ractor.recv, move: true)
end
end
workers = 4.times.map do |index|
worker = Worker.new(index)
Ractor.new(pipe, worker) do |pipe, worker|
loop do
job = pipe.take
puts "taken job from pipe by #{Ractor.current} and work it by worker #{worker.id}"
worker.work(job)
end
end
end
redis = Redis.new
loop do
puts "Waiting on work"
queue, job = redis.brpop(QUEUE_NAME)
puts "Pushing work from #{queue} to available workers"
pipe.send(job, move: true)
end
つっつきボイス:「この間のRactorでWebサーバー記事に続いてジョブスケジューラ記事も出ていました」「こういうブロック図なのね↓」
「もしかするとマイクロサービスを書くときには高いマルチスレッド性能と大量のリクエストを捌ける要件を満たすために今後Ractorが使われるようになるかもって思いました」
⚓defined?
キーワードはblock_given?
メソッドより速い(Ruby Weeklyより)
つっつきボイス:「そうそう、defined?
はメソッドではなくてキーワードなんですよね」「ここを理解しとかないとたまにハマる」
参考: クラス/メソッドの定義 (Ruby 2.7.0 リファレンスマニュアル) -- defined?
「で記事はblock_given?
メソッドとdefined?(yield)
のパフォーマンスを比較している」「言われてみればyield if defined?(yield)
っていう書き方は可能ですね」
# 同記事より
def method_with_defined_yield
yield if defined?(yield)
end
method_with_defined_yield {42} # => 42
method_with_defined_yield # => nil
「でdefined? yield
の方が1.3倍近く速い↓」「たしかにメソッド呼び出ししない分defined?
キーワードの方が速そうですね」「defined?(yield)
が使われているのを見たときにblock_given?
と同じ意味だとすぐわかるだろうか...」
# 同記事より
Warming up --------------------------------------
block_given? 988.926k i/100ms
defined? yield 1.259M i/100ms
Calculating -------------------------------------
block_given? 9.897M (± 1.1%) i/s - 50.435M in 5.096367s
defined? yield 12.561M (± 1.8%) i/s - 62.944M in 5.012616s
Comparison:
defined? yield: 12561217.9 i/s
block_given?: 9897436.6 i/s - 1.27x (± 0.00) slower
後でベンチマークコード↓をRuboCop(0.92.0)でチェックしてみたところ、yield if defined?(yield)
について特に怒られはありませんでした。
# frozen_string_literal: true
require 'benchmark/ips'
def method_with_block_given
yield if block_given?
end
def method_with_defined_yield
yield if defined?(yield)
end
Benchmark.ips do |x|
x.config(time: 5, warmup: 2)
x.report('block_given?') { method_with_block_given { 42 } }
x.report('defined? yield') { method_with_defined_yield { 42 } }
x.compare!
end
⚓down: ストリーミングダウンロードgem(Ruby Weeklyより)
つっつきボイス:「ダウン?」「ストリーミングダウンロード用のgemみたいです」「BASIC認証もやってくれるのね」「見た感じ、Rubyでファイルをストリーミングダウンロードしながらそれを開いたり処理を実行したりということもできるんでしょうね」
# 同リポジトリより
remote_file = Down.open("http://example.com/image.jpg")
remote_file.size # "Content-Length" ヘッダーから読み出す
remote_file.read(1024) # 最初の1KBをダウンロードして返す
remote_file.read(1024) # 次の1KBをダウンロードして返す
remote_file.eof? #=> false
remote_file.read # ファイルの残りをダウンロードして返す
remote_file.eof? #=> true
remote_file.close # HTTPコネクションをクローズして内部Tempfileを削除する
「こうやってチャンクに対してeachできる↓のを見ると、結構頑張って作った感じがする」
# 同リポジトリより
remote_file = Down.open("http://example.com/image.jpg")
remote_file.each_chunk { |chunk| ... }
remote_file.close
「チャンクというのは?」「HTTP 1.1のTransfer-Encoding: chunked
というもので、この機能を使うとファイルの特定の範囲を転送したり、いわゆるファイルダウンロードの『レジューム』ができたりします」「ふむふむ」「たとえばチャンクが1MB単位だとすると1MBごとにeachで処理するとか」「なるほど!」
参考: Transfer-Encoding - HTTP | MDN -- chunked encodingについて
chunked
データはチャンク (塊) の連続で送られます。この場合は Content-Length ヘッダーが省略されます。それぞれのチャンクの先頭に現在のチャンクの長さを16進数の形式で追加し、その後で '\r\n' が続き、チャンク自体ももう一つの '\r\n' が続きます。最後のチャンクは通常のチャンクですが、長さが0であるという点が異なります。この後に、一連のエンティティのヘッダーフィールド (おそらく空) から成るトレイラーが続きます。
developer.mozilla.orgより
「このdown gemは、Webアプリで使うというよりクライアントRubyで使うときに便利そうかな👍 : たとえばですけど、音声データのリストをストリーミングでダウンロードしながら音声の中身をRubyで解析して、ある条件に一致したら途中でもダウンロードを停止して次の音声データに進むとか」「なるほど、全部取ってこなくて済めばネットワーク帯域やディスク領域も節約できそうですね」
⚓ Rubyの!
付きメソッド
ということはつまり、「!」付きメソッドを定義する場合は「!」が付いていないメソッドが存在していることが前提条件になりますね。(「!」付きメソッドだけを定義するのは避けた方が良さそう) https://t.co/o1gyVLnn8U
— Junichi Ito (伊藤淳一) (@jnchito) September 28, 2020
つっつきボイス:「このツイート自分も見ました」「!
付きのメソッドの扱いをどうするかについては諸説ありますね」「基本的には!
付きメソッドがあるなら!
なしメソッドもペアとしてあるべき、という考え方でだいたいやれるかな」「ときどきペアにしにくいメソッドもありますけど」
「そういえば前に翻訳した記事↓で!
付きメソッドだけがある状態を『プリマドンナメソッド』って呼んでたのを思い出しました↓」「『思わせぶりだからプリマドンナ』なんですか?😆」「プリマドンナの由来は未だに裏が取れてませんけど😅」
「考えてみれば!
を使わなくても、メソッド名そのもので危険性を示すことも可能ですよね」「Linuxのrm
コマンドの--no-preserve-root
オプションみたいな名前にするとか」「その長〜いオプションを付けないとrm -rf /
を実行できないようにするヤツですね」「それでも入力に慣れるとうっかり実行しちゃったりするんですよ...😇」
参考: 実行してはいけないLinuxコマンド(2) Ubuntu Desktopで『rm -rf /』を実行 | マイナビニュース
「メソッド名の!
だけで危険を知らせるよりは、長い名前のような別の方法も活用するのがよい気がします」「たとえば危険な操作はno_preserve_root: true
みたいな引数を与えないと実行できないようにするとか、他の方法もいろいろ考えられますよね」「そのうえで@jnchitoさんも言うように、原則として!
ありメソッドと!
なしメソッドの定義がペアになるようにするのがよさそう」
⚓DB
⚓PostgreSQL 13リリースと新機能検証
つっつきボイス:「どちらもBPS社内Slackに貼っていただいた記事です」「13のGA版は少し前から出ていましたけど、このたびGAが取れて正式にリリースされました🎉」「虎の巻、PostgreSQL 11や12のときにも出ていたんですね」
「13はどこが変わったんでしょう?」「13にはいわゆる破壊的な変更はそれほどなかったと思いますけど、そこも含めてこの「虎の巻」記事に詳しく書かれてますのでそちらを読んでみるとよいと思います」「『B-Treeインデックスの重複排除機能がデフォルト有効に』とかよさそう」
⚓CSS/HTML/フロントエンド/テスト/デザイン
⚓Caddy: HTTP/2がデフォルトのWebサーバー
https://t.co/Svq4ClWcBc
Look great. Time to switch from nginx to caddy!— Tim Cheung (@timshingyu) October 4, 2020
つっつきボイス:「キャディ?」「HTTP/2がデフォルトのWebサーバーだそうなんですけど、その『Goodbye Nginx, hello Caddy』という記事で初めて知りました」「Nginxのオルタナっぽいですね」「Automatic HTTPSというのはTLS証明書を更新してくれるということみたい」
「Caddyには使いやすい設定がいろいろあるっぽい」「たしかにNginxのコンフィグは面倒ですよね」「Nginxのコンフィグはサイト構成が複雑になったりconfigファイルが肥大化してくると設定が難しくなってきます...」
# 同サイトより
example.com
# Templates give static sites some dynamic features
templates
# Compress responses according to Accept-Encoding headers
encode gzip zstd
# Make HTML file extension optional
try_files {path}.html {path}
# Send API requests to backend
reverse_proxy /api/* localhost:9005
# Serve everything else from the file system
file_server
⚓ Nginx
「Nginxのコンフィグって、あくまでコンフィグでしかなくてスクリプト的に動かない(=設定ファイルを上から順に評価していくわけではない)のが面倒...」「それほんまにそう思います: ngx_mruby↓とか入れたら違うかもしれませんけど、そこまでするかどうかというのもありますし」
「使ってみないとわかりませんけど、このCaddyは、凝った構成よりもWebサーバーとしての代表的な構成にしたい場合によさそうなのかなと推測してみました」「代表的な構成のリバースプロキシとして楽に導入できるとか、そういうのだといいですよね」「まあリバースプロキシとして使うだけならどれを使ってもいいと思います」
「やっぱりWebサーバーの選定って悩みますよね...ちなみにこの間h2o↓を使ってみました」「お〜使ったんですね!」「Railsの前段に何置こうかなとあれこれ考えて結局h2oにしました」
ちなみにRailsやDockerでのCaddyの利用記事もありました。
- 元記事: Setting up Rails with Docker and Caddyserver - DEV Community 👩💻👨💻
参考: V2: Serve Rails application with static assets - Help - Caddy Community
⚓言語/ツール/OS/CPU
⚓GitHub Code Scanningがリリース
Code scanning is here! 🎉
Prevent issues in code by automating security as a part of your workflow.
✔️ Free for public repositories
✔️ Developer-first, GitHub native
✔️ Enabled for GitHub Enterprise CloudLearn more! https://t.co/2SSCjb09Il
— GitHub (@github) September 30, 2020
つっつきボイス:「GitHub Code Scanningがpublicリポジトリで正式に利用できるようになった🎉」「とりあえず有効化しておくとよさそう」「どのぐらい怒られるかな...」「秘密鍵を登録してないかとかそのあたりをチェックされるかも」
「CodeQLという、GitHubが定義したDSLがあるのか↓」「おそらくRuboCopみたいに自分でルールを追加できるんでしょうね」「たとえばRails用のCodeQLがメンテされるようになったら嬉しいかも」
参考: CodeQL - GitHub Security Lab
# securitylab.github.comより
from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeDeserializationConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink,
"Unsafe deserialization of $@.", source.getNode(), "user input"
「CodeQLがオープンな規格ならGitLabでも使えることが期待できそうですけど、まだその辺の情報は見当たらないかな...BPSではGitLab↓がメインなのでその辺が気になってます」
後編は以上です。
バックナンバー(2020年度第4四半期)
週刊Railsウォッチ(20201005前編)Ruby 2.7.2がリリース、Shopifyのモジュラー化gem「packwerk」、stimulus_reflexほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。