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

週刊Railsウォッチ: ruby/debugをChromeでリモートデバッグ、Rubyアプリの最適化ほか(20211019後編)

こんにちは、hachi8833です。発表された新型MacBook Proのノッチが…

週刊Railsウォッチについて

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

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Ruby

🔗 ruby/debugのChrome用リモートデバッグ機能


つっつきボイス:「BPS社内Slackに貼っていただいたツイートです」「これはいい機能👍」

ruby/debug - GitHub

「これは?」「ruby/debugの中からHTTPサーバーを起動してそこにChromeでアクセスすると、Chromeからruby/debugを制御できるという、ruby/debugの新機能です」「お〜動画でChromeの中にデバッガコンソールが見えた」「いわゆるリモートデバッグ機能そのもの」「Chrome拡張を使うのかと思ったら不要なんですね」「Chrome Devtoolsで公開されているChromeの独自機能を使っているようです」

現時点ではmasterブランチに入っています。

🔗 Rubyアプリを最適化する(Ruby Weeklyより)


つっつきボイス:「ちょっと眺めた限りではシングルスレッドで複数のIOをチェインして同時処理させる話のようですね」

参考: class Thread (Ruby 3.0.0 リファレンスマニュアル)

Backend#chainを使うと、こんなふうにI/O操作をチェインできるらしい↓」

# 同記事より
Thread.current.backend.chain(
  [:write, @conn, "#{len.to_s(16)}\r\n"],
  [:splice, r, @conn, len],
  [:write, @conn, "\r\n"]
)

「このあたりのRESPOND_FROM_IO_PROGRAMの内側がインタプリタとかASTパーサーっぽく見える↓」

# 同記事より
# program references:
# 0 - headers
# 1 - io
# 2 - @conn
# 3 - pipe r
# 4 - pipe w
# 5 - chunk_size
RESPOND_FROM_IO_PROGRAM = [
  [:write, 2, 0],
  [:loop,
    [:splice, 1, 4, 5],
    [:break_if_ret_eq, 0],
    [:store_ret, :len],
    [:write_cte_chunk_size, 2, :len],
    [:splice, 3, 2, :len],
    [:write, 2, "\r\n"]
  ],
  [:write, 2, "0\r\n\r\n"]
]

def respond_from_io(request, io, headers, chunk_size = 2**14)
  formatted_headers = format_headers(headers, true, true)
  r, w = IO.pipe
  Thread.backend.submit(RESPOND_FROM_IO_PROGRAM, formatted_headers, io, @conn, r, w)
end

「お、同じ部分を今度はDSL化して読みやすくした感じですね↓」

# 同記事より
RESPOND_FROM_IO_PROGRAM = Polyphony.io_program(
  :headers, :io, :conn, :pipe_r, :pipe_w, :chunk_size
) do
  write :conn, :headers
  io_loop do
    splice :io, :pipe_w, :chunk_size
    break_if_ret_eq 0
    store_ret :len
    write_cte_chunk_size :conn, :len
    splice :pipe_r, :conn, :len
    write :conn, "\r\n"
  end
  write :conn, "0\r\n\r\n"
end

「最後のまとめを見ると、この記事ではトップの層でRubyのデータ構造を使い、I/O操作のような単純な処理は下の層のCで操作することで並列実行しやすくしたようですね」「Rubyでもこんなふうにすると速くできるよと」「Linuxのio_uringと似たアプローチとありますね」

本記事ではRubyアプリのパフォーマンス最適化のためにプログラムを2つの層に分離するアプローチを紹介しました。つまり、Rubyのデータ構造を用いて低レベル処理を表現するRubyのトップ層と、それらの処理を最適な形で実行するCの実装の層です。このアプローチでは、chunkedエンコーディングによるHTTPレスポンス送信、受信データ解析、I/Oのループ処理などの複雑な処理を長時間行う場合に特に有効です。
上述のようにこのアプローチはLinuxのio_uringで用いられているものと似ています。考え方は同じで、(I/O)操作をデータ構造で表現し、その実行を最適化された下の層(io_uringではカーネル、Rubyの場合はC拡張)に移行しています。
同記事より

参考: Transfer-Encoding - HTTP | MDN
参考: io_uringで高速IO処理(?) | κeenのHappy Hacκing Blog

「ところでさっきのRESPOND_FROM_IO_PROGRAMの中身はリテラルになっていますけど↓、こうするとRubyのインタプリタが仕事しなくて済むようになってJITが効きやすくなるのかもと思いました」「たしかにリテラルですね」

# 同記事より抜粋
RESPOND_FROM_IO_PROGRAM = [
  [:write, 2, 0],
  [:loop,
    [:splice, 1, 4, 5],
    [:break_if_ret_eq, 0],
    [:store_ret, :len],
    [:write_cte_chunk_size, 2, :len],
    [:splice, 3, 2, :len],
    [:write, 2, "\r\n"]
  ],
  [:write, 2, "0\r\n\r\n"]
]

🔗 HTTPI: RubyのHTTPクライアント向けの共通インターフェイス(Ruby Weeklyより)

savonrb/httpi - GitHub


つっつきボイス:「HTTPI、見たことなかった」「新しそうなライブラリですね」

# 同リポジトリより
require "httpi"

# create a request object
request = HTTPI::Request.new
request.url = "http://example.com"

# and pass it to a request method
HTTPI.get(request)

# use a specific adapter per request
HTTPI.get(request, :curb)

# or specify a global adapter to use
HTTPI.adapter = :httpclient

# and execute arbitrary requests
HTTPI.request(:custom, request)

「これは何をするものなんでしょう?」「Rubyにはこのサイトにも書かれているようなHTTPClientやNet::HTTPのようないわゆるHTTPクライアントライブラリがたくさんありますけど、それぞれのインターフェイスはまちまちなので、それらを統一して呼べるようにして、HTTPI.adapter = :httpclientみたいにアダプタを切り替えるだけでHTTPクライアントライブラリを切り替えられるということでしょうね」「なるほど、RubyのHTTPクライアントライブラリ向けのラッパーでしたか」

「ただ、HTTPクライアントライブラリを使い分ける機会はそうそうないと思いますけど」「ライブラリをこれと決めたら普通はそのまま使いますよね」「Active Jobを経由せずにSidekiqを直接使う話(ウォッチ20211018)と似ているかも」「MySQLからPostgreSQLに移行することがめったにないのもそうですね」「ライブラリごとに機能も違ってくるので、ライブラリ間の差異を共通化レイヤで吸収するのはそれなりに大変」

「HTTPIは新しいライブラリですし、今すぐproductionで使うものでもないので、もしかすると練習用として作ってみたのかなと想像してみました: こういうのを自分でやってみると楽しく勉強できると思います」「特定のHTTPライブラリのバグを切り分けるのに使えるかもしれませんね」

🔗 その他Ruby


つっつきボイス:「ruby/specリポジトリで、Ruby 3.0の新機能や機能変更のspecを書いて欲しいという募集だそうです」

#823を見ると、specを書いて欲しい3.0の機能がちゃんとリストになっているのがいいですね」「完了のチェックボックス、この間見たときより増えてるみたいです」「これならコントリビュートしやすそう」「やってみようかな」

その後もチェック済みは着々と増えているようです: Pull requests · ruby/spec

「お、IBM720なんてエンコーディングがあるんですって(#16233)」「聞いたことないですね」

参考: MFT で使用できるコード・ページ - IBM Documentation

🔗DB

🔗 SpannerにPostgreSQL互換インターフェイスが追加(Publickeyより)


つっつきボイス:「そうそう、GoogleのSpannerにこの機能が入りましたね: Spannerはかなり高価だったんですが、最近値下がりして以前より使いやすくなりつつあるので、PostgreSQL互換のインターフェイスが使えるようになればより手を出しやすくなりそう」

参考: Cloud Spanner  |  Google Cloud

「AWSにはRDSやAurora PostgreSQLがありますけど、これまでGCPにはマルチAZやフェイルオーバーまですべて備えたようなRDS的なサービスというとCloud SQLしかなかったと思うので、今回の発表でSpannerがGCPでの選択肢に入ってきそうですね」「なるほど」「AWSからSpannerを使おうとするとネットワーク的に遠いという問題はありますが」「GCPからSpannerを使う方が無理がなさそうですね」「レイテンシはその方がよいでしょうね」

参考: Amazon RDS(マネージドリレーショナルデータベース)| AWS
参考: Amazon Aurora PostgreSQL の特徴 | MySQL PostgreSQL リレーショナルデータベース | アマゾン ウェブ サービス
参考: Cloud SQL ドキュメント  |  Google Cloud

「Spannerはスケーラビリティがすごく高くてオートスケールも強いんですが、これほどの高性能が必要な案件はなかなかないでしょうね」「あ〜」「あるとすれば、ソシャゲやワクチン予約受付サイトのようにユーザー数がいくらでも増える可能性やアクセスが短時間にものすごく集中する可能性もあって、かつランニングコストに見合うサービスかな」「なるほど」「そのぐらいの規模になるとAWSのオートスケールだとアクセス急上昇に間に合わないかも」「ソシャゲだとSpannerはちょっともったいなさそうですけどね」

「その意味では、AWSのAurora PostgreSQLは負荷に応じてオートスケールできて、しかもRDSと値段がほとんど変わらないのがいいんですよ」「たしかに値段がほぼ同じなら迷わずAurora PostgreSQLを選ぶでしょうね」「SpannerはDBインスタンスを立てるのに比べて安くないので悩ましいところ」

「まだGCPやSpannerは本格的に運用したことはありませんが、Spannerは選択肢のひとつとしておくとよさそう👍」


「ところで元記事ではSpannerがNoSQLとして紹介されていたけど、NoSQLだったかな?」「公式ブログ↓を見ると、当初はNoSQLキーバリューストアとして設計されたけどリレーショナルモデルも採用したとありますね」「なるほど、自分の中ではSpannerは無限といってもいいぐらいにスケールできるRDBという位置づけだったけど、合ってるみたいでよかった」

参考: NoSQL から新しい SQL へ : グローバルなミッションクリティカル DB へと進化を遂げた Cloud Spanner | Google Cloud Blog

🔗クラウド/コンテナ/インフラ/Serverless

🔗 AWS Lambdaバトル


つっつきボイス:「これは自分も見ましたけど、ベンチマークのソースコードを見るとJSONを取ってきてDynamoDBに格納しているだけで、実行環境での起動以外ではほとんどリソースを使っていないんですよ」「そんなに軽い処理なんですか?」「コードに分岐すらありません」

Aleksandr-Filichkin/aws-lambda-runtimes-performance - GitHub

# aws-lambda-runtimes-performance/ruby-lambda/app.rb
require 'json'
require 'aws-sdk-dynamodb'

 $client = Aws::DynamoDB::Client.new

def create(event:,context:)
  body = event["body"]
  book =JSON.parse(body)
  id = SecureRandom.uuid
  book["id"]=id

  table_item = {
    table_name: "book",
    item: book
  }
  $client.put_item(table_item)
  { statusCode: 201, body: JSON.generate(book) }

end

参考: Amazon DynamoDB(マネージド NoSQL データベース)| AWS

「このベンチマークは実行環境のプロセス起動を比較しているようなものなので、ビジネスで使うサービスを運用するうえではあまり参考にならないかなと思います」「なるほど」「Rubyのグラフはメモリーリークっぽく上昇してはいますけど、自分の印象ではRuby十分速いと思いました」

「ちゃんとベンチマークするのは難しい…」「言語が違えば実装も変わりますし、特にJSONパーサーはライブラリによって速度がかなり変わります」「それもそうですね」


なお、LambdaバトルでNodeが遅い件について、AWS_NODEJS_CONNECTION_REUSE_ENABLEDがオンになっていない可能性があるのではというissueが立っている↓とBPS社内メンバーからメンションをもらいました。

参考: Optimize NodeJS function warm starts by enabling TCP connection reuse by bilalq · Pull Request #6 · Aleksandr-Filichkin/aws-lambda-runtimes-performance

🔗JavaScript

🔗 jQuery UIとjQuery Mobileが開発終了(Publickeyより)


つっつきボイス:「今回はたまたまPublickeyの記事が多めになりました」「jQuery UIとjQuery Mobileがついに」

「ツイート↓で見かけましたけど、jQueryは今でもすごく使われているんですね」「実際jQueryで十分な場合も多いですし、ライブラリの種類も豊富だし、デザイン系も含めるとjQueryを扱える人は多いですね」「なるほど」

「jQueryがこれだけ人気を得た理由のひとつは、複数ブラウザで共通に利用できたことでしょうね: 今はその役割はほぼ終わったと思いますが」「そうですね」「蓄積されたライブラリは簡単にはなくならないと思います」

「jQueryは複数ブラウザで使えるJSライブラリのさきがけだったんでしょうか?」「Prototype.jsも同じぐらいの時期だったかな: 一時期はPrototype.jsが優勢だったこともありましたけど今は消えた」「Prototype.jsは2015年までメンテされてたんですね」

参考: Prototype JavaScript Framework - Wikipedia

「そういえばjQueryとPrototype.jsを両方置くと名前空間が衝突するのを思い出した↓」「そうだったかも」「どちらも$を使うからですね」「jQuery.noConflict();、なつかしい」

参考: prototype.jsと同時に使うには - jQuery 日本語リファレンス

🔗言語/ツール/OS/CPU

🔗 計算量とオーダー


つっつきボイス:「はてブでバズっていました」「O(m)みたいに書くビッグオー記法以外にもΘΩとかいろんな記法があるんですね」「私もビッグオーしか知りませんでした」「みっちり書かれてる」

参考: ランダウの記号 - Wikipedia

「計算量やオーダーの概念はプログラマーにとって大事ですし、アルゴリズムごとにオーダーもありますけど、現実のユースケースだとデータや検索にlocality(局所性)が絡んだりして、きれいに正規分布しないことも多いですよね」「そうそう」「アルゴリズムの特徴を知っておくのは大事だけど、計算量を覚えるだけだとあまり有効でない気がします」

参考: 参照の局所性 - Wikipedia

「具体的な計算量はググればわかるので、むしろ計算量という概念があることは知ってて欲しいですよね」「コードレビューで3重ネストがあったときに、これだと計算量がこのぐらいになるから早期脱出を入れようとか、そういうやりとりはしますね」「そうそう、丸暗記しなくてもいいので、コードに関してやりとりするときにその視点は持ってて欲しい」

「計算量やオーダーは知っていて損はしないので読んでおくといいと思います👍」


後編は以上です。

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

週刊Railsウォッチ: Ruby 3.1にYJITマージのプロポーザル、Rubyのmagic historyメソッド、JSのPartytownほか(20211012後編)

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

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

Ruby Weekly

Publickey

publickey_banner_captured


CONTACT

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