- Ruby / Rails関連
週刊Railsウォッチ(20191209前編)Pumaのphased-restartとUnicornのgraceful restart、RailsのTZハックが不要になった話ほか
こんにちは、hachi8833です。ChainerがPyTorchに乗り換えられたそうです。
Preferred Networks、深層学習の研究開発基盤をPyTorchに移行 | 株式会社Preferred Networks https://t.co/rytkSs6z7c おお。正しい判断ではないでしょうか。お疲れ様でした。
— Yuta Kashino (@yutakashino) December 5, 2019
つっつきボイス:「ついさっき上のツィートが流れてきました🛶」「Chainerといえばメジャーな機械学習フレームワークだったのに」
参考: Chainer を振り返って
「中の人のブログを見るとメンテナンスモードに移行するってあるし😳」「機械学習やってる人には結構大きな変更でしょうね〜」
「Rubyで機械学習やる人は最近増えてきましたけど、RubyからPythonのライブラリを呼べるpycall.rbで有名なmrknさんがSciRubyというプロジェクト↓をやっていますね」「おぉ」「pycallはRubyからPythonライブラリ経由でPythonコードにアクセスして、Python側で作ったデータにRubyからアクセスできるようにするというものですね: 同じメモリ内でやるのでデータをメモリコピーしなくていい😋」
「mrknさんは最近だとデータ処理系のApache Arrow↓もやってたりしますね(ウォッチ20190402)」
- サイト: Apache Arrow
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください
今回は週刊Railsウォッチ第17回公開つっつき会を元にお送りいたします。定員枠を初めて広げました。お集まりいただいた多くの皆さま、ありがとうございました!😂🙇
⚓Rails: 先週の改修(Rails公式ニュースより)
⚓ActiveSupport::Duration#inspect
の結果がおかしいのを修正
- PR: The return value of `ActiveSupport::Duration#inspect` is wrong when its parts are empty by ttanimichi · Pull Request #37839 · rails/rails
- 関連PR: Fix `since` and `ago` with a duration which has empty parts by kamipo · Pull Request #37849 · rails/rails
- 関連PR: Duration created with no parts will have a default seconds part eqaul to 0 by kinnrot · Pull Request #31310 · rails/rails
- 関連PR: Should keep the original parts for zero duration by kamipo · Pull Request #37882 · rails/rails
- issue: `0 % ` returns unexpected result · Issue #31302 · rails/rails
# 同PRより
(1.day / 24).inspect #=> "0 seconds" # inspectの結果がおかしい
(1.day / 24).to_i #=> 3600
(1.day / 24) == 1.hour #=> true
(1.hour).inspect #=> "1 hour"
(1.hour).parts #=> {:hours=>1}
(1.day / 24).parts #=> {}
つっつきボイス:「Active SupportのDuration周りのバグだったようです」「ActiveSupport::Duration
は、名前のとおり期間を抽象化したものですね」
assert_equal "3600 seconds", (1.day / 24).inspect
「追加されたテストコード↑を見ると、1.day / 24
をinspect
したら3600秒になるべきだったのに0秒が返ってたのか〜」「他にもいくつか類似の修正PRがありましたので上に貼っておきました」「Durationとしては分でも秒でも同じ長さのはずなんでしょうけど、内部状態の更新あたりが微妙に失敗してたのかもしれませんね😅」「単位が絡むと面倒そう...」
# activesupport/lib/active_support/duration.rb#L377
def inspect #:nodoc:
- return "0 seconds" if parts.empty?
+ return "#{value} seconds" if parts.empty?
parts.
sort_by { |unit, _ | PARTS.index(unit) }.
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
to_sentence(locale: ::I18n.default_locale)
end
「Railsでは時刻から時刻を引くとDurationになるんでしたっけ?」「そうそう、Time同士の差を取るとActiveSupport::Duration
オブジェクトになりますね☺️」
⚓redirect_to.action_controller
の通知のペイロードにActionDispatch::Request
を追加した
- commit: Add request to the payload for notifications to redirect_to.action_co… · rails/rails@23b7382
| Key | Value |
| ----------- | ----------------------------- |
| `:status` | HTTP response code |
| `:location` | URL to redirect to |
| `:request` | The `ActionDispatch::Request` |
つっつきボイス:「これはredirect_to
の引数が増えたのかと思ったらinstrument
が出てきてるからログ周りの話か」「あ、なるほど」「instrumentationはログとは少し違いますが、redirect_to.action_controller
のinstrumentationでrequest
も拾えるようになったということだと思います☺️」
# #L
def redirect_to(*)
- ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
+ ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
result = super
payload[:status] = response.status
payload[:location] = response.filtered_location
result
end
end
⚓RailsのInstrumentationを「バス」で理解する
「Instrumentation(測定、計測といった意味)について簡単に説明しますね: Railsではpub/sub(パブリッシャー/サブスクライバ)的な方法でInstrumentationが使えるんですけど、上のようなパブリッシャーをサブスクライバで拾うような形になります」
「pub/subってどう説明するのがいいかな...いわゆる差し込みフリータイプの電源タップ↑では電源プラグをどこにいくつ挿してもよくて、誰も使っていなくても電気が常に流れていますけど、コンピュータなんかにおける『バス』↓という概念もそれに似ていて、pub/subはそういうバスで考えるとわかりやすいと思います」「おぉ〜」
「Instrumentationではパブリッシャー(pub)がこういうバス的なところにいます: バスに誰もいなくても大勢いいてもお構いなしで」「そしてそのバス的なところにサブスクライバ(sub)が登録されていればそいつらがpubからメッセージを拾える、というような形になります」「ふむふむ」「おおむねそんな感じだったと思います😆」
「InstrumentationはRailsのログ出力にとても良く使われる仕組みですね: いろんなレベルのログを片っ端から出力すると遅くなるので、ログレベルに応じて自分の欲しいものだけをサブスクライブすることで、負荷を上げずにログを柔軟に取れるようにしているわけです」「Railsフレームワークのコードでinstrument
って出てきたらそこがログに出せるポイントになっているということです」
はみ出し:「そういえばInstrumentationっていう用語はWMI↓で初めて知ったんですけど、長ったらしくて収まりが悪いせいか未だに日本語にもカタカナにもなってなくて、Railsガイドでも英語表記で統一しました」「pub/subの方がしっくりくるな〜😆」「instrumentって楽器?」「元々は機材とか装置みたいな意味なんですけど楽器の意味もあって、musical instrumentと書けば確実に楽器ですね」
参考: Windows Management Instrumentation - Wikipedia
⚓service_url
が非推奨化、今後はurl
に
Variant#service_url
とPreview#service_url
を非推奨化に。
今後はBlob
と一貫するurl
を使う
同PRより大意
つっつきボイス:「Active Storageの改修だそうです」「Variant
とPreview
ってのがあるのね😳」「Active Storageをまだ使ってない身としてはそうですか〜としか言えないし😆」「差分を見た感じではservice_
が冗長だったってことなんでしょうね☺️」「使う人が増える前にメソッド名直しちゃえという感じなのかも☺️」「service_
以外のurl
がないならurl
だけでええやろってことかな😆」
「Active StorageはRails 6でrails new
するなら使ってもいいんじゃないかと思いますね: 前のRailsから移行してActive Storageにも移行するのは大変そうですけど😆」
⚓URIかURLか
「めちゃ細かいこと言うとurl
でいいんだろうかと🤣」「uri
じゃないのかと😆」
「もともとUniform Resource Locatorの略でURLですけど、もう随分前に『本来やりたかったのはlocaltorみたいな物理的な場所ではなく識別子(identifier)だよな』ってURI(Uniform Resource Identifier)を使うべきという空気になったのに、未だにURLが広く使われているという🤣」「URL消えませんね🤣」「もう滅びない感🤣」
「ただRFCではURLもURIも一応両方あると思います☺️」「そうでしたか😳」「でURIがURLを包含していた気がする...この辺かな↓」「URIの方が意味が広いんですね」「URLは//:@:/
↓みたいなよく見かける形」
//:@:/
「でURIはtel:+1-816-555-1212
みたいなのも含む↓」「これはURIだけどURLではないと」「気になる人はこの辺のRFCを読んでみるといいと思います☺️ 」
The following example URIs illustrate several URI schemes and variations in their common syntax components:
ftp://ftp.is.co.za/rfc/rfc1808.txt
http://www.ietf.org/rfc/rfc2396.txt
ldap://[2001:db8::7]/c=GB?objectClass?one
mailto:John.Doe@example.com
news:comp.infosystems.www.servers.unix
tel:+1-816-555-1212
telnet://192.0.2.16:80/
urn:oasis:names:specification:docbook:dtd:xml:4.1.2
参考: Uniform Resource Identifier (URI): 一般的構文 -- RFC3986 日本語訳の複製
URI は、それが位置指定子か、名前か、あるいはその両方かという点において、更に分類できる。 "Uniform Resource Locator" (URL) という用語は、リソースを識別するのに加えて、その主なアクセスメカニズム (例えば、そのネットワーク上の "位置") を記述する事によってリソースの場所を見つける方法を提供するような、URI の部分集合を指す。 "Uniform Resource Name" (URN) という用語は、例えそのリソースが存在しなくなったり、あるいは利用不可能になっても全体において一意で永続的である事が要求される "urn" スキーム [RFC2141] の下での両方の URI、また名前の特性を持つあらゆる他の URI を参照するために歴史的に使用されている。
triple-underscore.github.ioより
⚓HashWithIndifferentAccess#convert_value
のアロケーションを削減
productionのプロファイリングで
ActiveSupport::HashWithIndifferentAccess#convert_value
がかなり高いようなので調べたところ、妙なbinding
アクセスを見つけた。
履歴を調べたところ、#36758と@64a4301あたりが由来らしい。
元々、キーワード引数をホットスポットで用いることでアロケーションを削減するのが狙いだったが、for
がキーワードなのでbinding.local_variable_get
でしのいでいた。
しかしbinding
は呼び出しのたびに新しくBinding
をアロケートするのでかなり遅くなる。
convert_value
はprivateメソッドなので、引数をリネームするだけでこの辺りの問題を回避できると思われる。
同PRより大意
# activesupport/lib/active_support/hash_with_indifferent_access.rb#L365
private
...
- def convert_value(value, for: nil) # :doc:
- conversion = binding.local_variable_get(:for)
-
+ def convert_value(value, conversion: nil) # :doc:
if value.is_a? Hash
if conversion == :to_hash
value.to_hash
else
value.nested_under_indifferent_access
end
elsif value.is_a?(Array)
if conversion != :assignment || value.frozen?
value = value.dup
end
- value.map! { |e| convert_value(e, for: conversion) }
+ value.map! { |e| convert_value(e, conversion: conversion) }
else
value
end
end
参考: ActiveSupport::HashWithIndifferentAccess
つっつきボイス:「お、HashWithIndifferentAccess
出たな😆」「😆」「便利なんですけど、とにかく名前長い😆: IDEなら補完が効くからまあいいんですけど」
ここでいったん録音が途切れました🙇
「...binding.local_variable_get
はいわゆるリフレクション的なアプローチで、本来のオブジェクト指向的なアクセスではなく、今動いているRubyのプロセスからアクセス権限とか全無視で無理やりぶっこ抜けます😆」「😆」「こういうRubyインターナルなものはやってて楽しいんですけど、やりすぎるといろいろぶっ壊れます😇」
参考: リフレクション (情報工学) - Wikipedia
「改修の方は今時間なくてちゃんと見てませんが、binding.local_variable_get
みたいな方法で速くしていたのが裏目に出て遅くなるのはよくあるパターンかも☺️」
⚓ネストしたconfig_for
ハッシュへの非シンボルアクセス(非推奨)を削除
# railties/lib/rails/application.rb#L222
def config_for(name, env: Rails.env)
if name.is_a?(Pathname)
yaml = name
else
yaml = Pathname.new("#{paths["config"].existent.first}/#{name}.yml")
end
if yaml.exist?
require "erb"
- config = YAML.load(ERB.new(yaml.read).result) || {}
- config = (config["shared"] || {}).merge(config[env] || {})
+ config = YAML.load(ERB.new(yaml.read).result, symbolize_names: true) || {}
+ config = (config[:shared] || {}).merge(config[env.to_sym] || {})
ActiveSupport::OrderedOptions.new.tap do |options|
- options.update(NonSymbolAccessDeprecatedHash.new(config))
+ options.update(config)
end
else
raise "Could not load configuration. No such file - #{yaml}"
end
rescue Psych::SyntaxError => e
raise "YAML syntax error occurred while parsing #{yaml}. " \
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
"Error: #{e.message}"
end
つっつきボイス:「これはdeprecatedな機能を削除したのね」
⚓Railsのdeprecationプロセス
「ちょっと解説すると、基本的にRailsでは何かの機能をなくすときに、いきなり消すのではなくて、まずdeprecation warningをログに出力するコードを追加して、次のメジャーバージョンアップで実際に消すという形で進めていきます」「みんなはそれを見て、消される前に対応することになりますね」
⚓connected_to
のキーワード引数database
を非推奨化(代替なし)
connected_to
のキーワード引数database
はシャーディングのキーとして想定されていない場合に大量のバグが発生する。database
キーワード引数が使われているテストとユースケースを検討した結果、これは削除すべきという結論に達した。
今後シャーディングをサポートする計画はあるが、それまでdatabase
キーワード引数は意味なしとなる。コネクションを新たに作成するアプリではestablish_connection
やconnects_to
を利用できる。
connected_to
でdatabase
キーワード引数をリクエスト中または使い捨てでないコネクションで使うと、database
というキーでバグの原因となる。
同PRより大意
# activerecord/lib/active_record/connection_handling.rb#L98
def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
+ if database
+ ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 6.2.0 without replacement.")
+ end
+
if database && role
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
elsif database
if database.is_a?(Hash)
role, database = database.first
role = role.to_sym
end
db_config = resolve_config_for_connection(database)
handler = lookup_connection_handler(role)
handler.establish_connection(db_config)
with_handler(role, &blk)
elsif role
if role == writing_role
with_handler(role.to_sym) do
connection_handler.while_preventing_writes(prevent_writes, &blk)
end
else
with_handler(role.to_sym, &blk)
end
else
raise ArgumentError, "must provide a `database` or a `role`."
end
end
つっつきボイス:「シャーディングでうまくいかないことがあったみたいです」「a lot of bugsですか😆」
「ちょっと説明すると、Rails 6ではマルチプルデータベース機能が入ったんですけど、たとえばマスターとレプリカがある場合にconnected_to
を使ってそのブロックの中でだけ一時的に接続先を変更することができるようになっていて、そこがシャーディングで不具合が起きてたということですね☺️」
「シャーディングをどのレイヤでやっているかにもよりますね: 最近のPostgreSQLだとデータベースレベルでシャーディングができたりしますし」「Railsは6.1でシャーディングをサポートするつもりらしいのでそのときに考えるということのようです」
「Railsでシャーディングをやりたいことって結構あるんでしょうか?」「それはもうめちゃありますよ: Railsでやるかどうかは別として、シャーディングしないとやっていけないようなユースケースはいろいろあります☺️」「なるほど」「まあシャーディングをgemでやるのかRailsの機能に入れるのかというのは議論の余地がありそうですけど、ユーザーがどれだけいるかわからないようなAction MailboxもRailsに機能として入ってくるぐらいなので、たぶんシャーディングもRailsに入るんでしょうね☺️」
⚓シャーディングについて
「シャーディング(sharding)はデータベースでは水平分割とも言われる手法ですね: 雑に説明すると、たとえばIDが奇数のデータベースとIDが偶数のデータベースを用意しておくと、IDで検索したときにきれいに分散アクセスできる、みたいな感じです」「おぉ」「ソシャゲ開発の全盛期にはシャーディングがよく使われていましたけど最近あまり聞きませんね: 単に普及して当たり前になったのか、実はつらくて止めたのかは知りませんけど😆」
参考: 分割 (データベース) - Wikipedia
参考: シャーディング - Qiita
⚓はみ出し: キーワード引数とRails
kwargs受けるかもしれないし受けないかもしれないblockで引数をdelegateしたいやつ(e.g. -> (*args, **kwargs) { target(*args, **kwargs) })もblockにruby2_keywords書けないからif kwargs.empty?で分岐するしかないって話になってマジかーってなってた
— Ryuta Kamizono (@kamipo) December 4, 2019
具体的にいうとたとえばこの行ですhttps://t.co/wIpCfkvaf7https://t.co/UnSYCD3abA
— Ryuta Kamizono (@kamipo) December 5, 2019
「以下は最初上のconnected_to
のキーワード引数の話かな?と思ってつっつきで引用してみましたが、別の箇所でした😅」「最近話題の、Ruby 2.7でキーワード引数周りのbreaking changesに絡んだ話ですね☺️」「こちらの翻訳記事をどぞ↓」
⚓Rails
⚓RailsのTZハックが不要になった話
ブログ公開後わずか2日でハックが不要に。#ITnewshttps://t.co/oXZVeprDYc
— Yukihiro Matz (@yukihiro_matz) December 3, 2019
- 元記事: 環境変数を設定するだけでRuby on Railsサーバが10%高速化する(かもしれない)話 - Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)
- 元記事: RailsでTZ環境変数を設定するハックを不要にした話 - Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)
つっつきボイス:「これはTwitterで見かけましたね☺️」「2本立ての記事で、TZ
環境変数を設定したらRailsが速くなったけど実はRuby 2.6で解決されていたのでハック不要だったというお話」「おぉ」「これはいい話でしたね〜😋」「はてブでも結構上がってたかも」
⚓jnchitoさんの振り返り記事
ここと次の記事で録音が途切れていました🙇。
久々に読み返してみて「やっぱりヒドいww」って思った。
ひどいコードをメンテしてきたからこそ実感する、良いコードや良い設計の大切さ - give IT a try https://t.co/MxIQC6pTa1
— Junichi Ito (伊藤淳一) (@jnchito) December 4, 2019
⚓Webpackerでやらかしがちなミス
# 同記事より
app/
javascript/
packs/
application.js
components/ # lots of files
images/ # lots of files
stylesheets/ # lots of files
...
つっつきボイス:「WebpackerのOverpackingってわかる😆」「とってもありそう😆」「詰め過ぎ💼」
シンプルなルール: Webpackerのpacksディレクトリのファイルのうち、対応する記述がアプリケーションの
javascript_pack_tag
にないものはoverpackingである。
同記事より抜粋
⚓Rails 6の細かな新機能をひたすら試す記事
- 記事リスト: suketa - Qiita
つっつきボイス:「ひとつひとつの記事は短いですけど、Rails 6のリリースノートに載っているかいないかみたいな細かい機能をひたすら試していてびっくりしました😳」「これは凄いな〜👍」「110件目って...」
今年の4月からずっとやってるんですね😳。
⚓Pumaのphased-restart
- 元記事: Deploying a Rails App With No Downtime and No Hanging Requests
- 参考: PumaのPhased Restartで環境変数を再読込する - shuheiktgw’s Blog - Medium
つっつきボイス:「Pumaサーバーにphased-restartというのがあるってこれで初めて知りました」「Unicornサーバーのgraceful restartとは別なのかな?Pumaだとphased-restartをするとダウンタイムなしでコードを再読み込みできるということか」
puma.ioより
* サイト: unicorn: Rack HTTP server for fast clients and Unix
「サーバーの再起動周りは何かとややこしい😅」「日本語記事の方を見た感じではPumaのphased-restartはmaster processを使い回し、新しいworker processはサーバー起動時のmaster processからforkされるとある: これだけ見るとUnicornのgraceful restartと同じような感じ?🤔」「Unicornのgraceful restartは、たしかmaster processが死ぬんだった覚えがありますね🤔」「お〜、そうでしたっけ?」「だったはずです」「ということはPumaのphased-restartはUnicornのとは違うということなんでしょうね」
- Unicornドキュメント: Signal handling
参考: Unicornの自動起動設定 | MMMブログ
⚓Railsサーバーの再起動について
「Unicornサーバーの再起動はあんまりいい思い出がないんですが😭、ちょっとだけこの辺の話をしますね」「はい〜😋」
「RailsのWebサーバーとして使われるUnicornやPumaは、起動するとまずmaster processが立ち上がって、外からのリクエストはたしかいったんmaster processが受けてそこからworker processをforkしてそこに振る形になります」「おぉ」
「こういうサーバーの再起動で何が問題になるかというと、上の記事にもあるように環境変数の更新なんですね😆: ご存知のとおり、Linuxでは親プロセスが死ぬとそこからforkした子プロセスも死ぬ仕組みになっているんですが、トランザクショナルな処理を実行中のworkerがいるときにmasterを殺してしまうと処理中のworkerも死んでデータが不整合になってしまうわけです😅」
「その辺を回避する仕組みがたとえばUnicornのgraceful restartで、中途半端な状態でworkerが殺されないようにいい感じに処理中のworkerを避けて落としていって、処理中のworkerがいなくなったら晴れて新しい環境に入れ替わることになってるんですが、実際には期待どおりに動かないこともあって😅」「😅」
「一般にサーバーの再起動は重たい処理なので、とてもリクエスト数の多いRailsサーバーをカジュアルに再起動するとその間サービスは止まりますし、再起動でメモリがいったん全解放されてしまうのでサービス復帰まで時間かかります」「他にも、リクエストの多いRailsサーバーを普通に再起動するとリクエストのキューが大量にたまって、復帰したけどリクエストをさばききれなくなってサーバーが繰り返し落ちるなんてこともあったりします😇」「このあたりはH2Oの作者のブログなどに詳しく載っていたと思います↓」
⚓その他Rails
先ほど見つけたツィートです。
#Railsチュートリアル解説動画 にギフト機能が追加されました🎁✨
これから学習をされる方、応援したい方へご活用していただけると嬉しく思います…🎅💝https://t.co/JuzLXHX415 pic.twitter.com/j0m43oylS9
— Railsチュートリアル 🎓 (@RailsTutorialJP) December 9, 2019
前編は以上です。
バックナンバー(2019年度第4四半期)
週刊Railsウォッチ(20191204後編)Rubyコードをトランスパイルするruby-next、Cloud Run正式リリース、2019年Web年鑑レポート、V言語ほか
- 20191202前編 Rails 6のimplicit_order_columnはカスタマイズ可能、rubocop-rails 2.4.0リリース、Capistrano記事ほか
- 20191119後編 メソッド参照演算子が廃止、GitHub新機能続々、平成Ruby会議、GitHub OAuthバイパスほか
- 20191118前編 ActiveJob引数のログ抑制、RailsガイドProプランお試し、ファイルアップロードのレジュームgemほか
- 20191112後編 invisible gemで可視性を変えずにパッチ当て、スライド:「型なし言語のための型」、自然言語の言語名を推測ほか
- 20191111前編 Active Recordモデルをprivateで封じ込める、心折れないRailsスキーマ管理、Railsセッションをクロスドメイン共有ほか
- 20191106後編 holiday_japan gemで日本の祝日判定、小さい関数が有害になるとき、Gitブランチのファジー検索ほか
- 20191105前編 Rails 6のデフォルト設定解説、DHHも消したいaccepts_nested_attributes_for、スライド『実践Railsアプリケーション設計』ほか
- 20191029後編 Ruby 2.7.0-preview2、tapping_device gemとhumanize gem、平成Ruby会議ほか
- 20191028前編 RailsにSTI用メソッドsti_class_forとpolymorphic_class_forが追加、RuboCopを変更箇所だけにかけるgem、strftime書式生成サイトほか
- 20191021 Rails 6でhas_many関連の修正やSprockets 4.0対応、Shrine 3.0がリリース、Minitestスタイルガイドほか
- 20191015 スライド「Rails Performance issues and Solutions」を見る、dirtyに*_previously_was が追加、Sidekiq 6.0.1ほか
- 20191008後編 Ruby 2.7のInteger#[]でバイナリチェック、rubyzip gemは強力、13KBのJavaScriptゲームほか
- 20191001後編 RedisとRubyをつなぐredis-object gem、Fullstaq Rubyの新バージョン、COUNT(*)とCOUNT(1)の速度ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。