ISUCON合わせで作ったやつ第n弾です。*1
モチベーション
達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践:書籍案内|技術評論社(通称ISUCON本) を読んでた時に
SELECT `posts`.`id`, `posts`.`user_id`, `posts`.`body`, `users`.`account_name` AS `users.account_name`, `users`.`authority` AS `users.authority`, `users`.`del_flg` AS `users.del_flg` FROM `posts` INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
のようにカラム名ドットが含まれるSELECT文を https://github.com/jmoiron/sqlx に渡すと users
の部分をいい感じにGoのstructに詰め替えてくれると書かれていたのですが、Rubyでも同じことをやりたくてモンキーパッチgemを作りました。
使い方
READMEが全てなんですが、 using Mysql2::NestedHashBind::QueryExtension
を書いたスコープで Mysql2::Client#query
と Mysql2::Client#xquery
にモンキーパッチを仕込みます。
require "mysql2-nested_hash_bind" using Mysql2::NestedHashBind::QueryExtension db = Mysql2::Client.new( host: ENV.fetch("MYSQL_HOST", "127.0.0.1"), port: ENV.fetch("MYSQL_PORT", "3306"), username: ENV.fetch("MYSQL_USERNAME"), database: ENV.fetch("MYSQL_DATABASE"), password: ENV.fetch("MYSQL_PASSWORD", ""), charset: "utf8mb4", database_timezone: :local, cast_booleans: true, symbolize_keys: true, reconnect: true, ) rows = db.query(<<~SQL) SELECT `posts`.`id`, `posts`.`user_id`, `posts`.`body`, `users`.`account_name` AS `users.account_name`, `users`.`authority` AS `users.authority`, `users`.`del_flg` AS `users.del_flg` FROM `posts` INNER JOIN `users` ON `posts`.`user_id` = `users`.`id` SQL rows.first #=> {:id=>1, :user_id=>445, :body=>"test", :users=>{:account_name=>"sue445", :authority=>false, :del_flg=>false}}
頑張りポイント
ISUCONで利用するということでパフォーマンスチューニングを頑張りました。
具体的には https://github.com/evanphx/benchmark-ips でベンチマークをとりつつ https://github.com/tmm1/stackprof でgemのボトルネックを調べてボトルネックになってた箇所を改善してます。
stackprofで見つかったボトルネックの1つに Symbol#to_s
があったのでfreezeされたStringを返す Symbol#name
を使おうとしたのですが、Symbol#name
が使えるのがRuby 3.0以降だったのでこのgemもRuby 3.0以降でしか使えないようにしてます。
ベンチマークのコードとレポートは https://github.com/sue445/mysql2-nested_hash_bind/tree/main/benchmark に置いているのでどれくらいオーバーヘッドあるかはここを見てください。