Ruby 3.1 での each_cons と each_slice の (コーナーケース?) 非互換変更

先日、RuboCop の ruby-head CI が落ちていたので、bugs.ruby-lang.org にフィードバックしていたイシューが以下。

bugs.ruby-lang.org

osyo さんがコメントで教えてくれた PR が以下で、each_conseach_slice の戻り値が nil から self に変わっているというものだった。

github.com

以下は破壊的変更が起きる (単純化した) サンプルです。ブロックで条件によって break を使った戻り値を使うか、each_cons としての戻り値を使うか判定するようなロジックが遭遇したケースです。

Ruby 3.0 以下

% ruby -v
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin19]

% irb
irb(main):001:0> [1, 2, 3].each_cons(2) { |i| break i if true}
=> [1, 2]
irb(main):002:0> [1, 2, 3].each_cons(2) { |i| break i if false}
=> nil

Ruby 3.1

% ruby -v
ruby 3.1.0dev (2021-10-26T00:30:42Z master 7d4c59203f) [x86_64-darwin19]

% irb
irb(main):002:0> [1, 2, 3].each_cons(2) { |i| break i if true}
=> [1, 2]
irb(main):003:0> [1, 2, 3].each_cons(2) { |i| break i if false}
=> [1, 2, 3]

たしかに振る舞いとして戻り値 nil より自然になる気もするが、Ruby 3.1 向けに進んでいる NEWS などで見かけなかったので、影響をどれくらい加味された変更かよく分かっていなかった。

Rails/OSS パッチ会で osyo さんと NEWS にあるといいですねと話していて、osyo さんがパッチを出してくれたのが以下。

github.com

ということで、こういったケースで Ruby 3.1 での非互換変更として影響を受ける可能性があるので、いちおう each_conseach_slice で同様のことを行っていないか見ておくと良いです。

(なお、最初に遭遇した RuboCop の方はそもそものロジックが 🤔 な部分もあったので、Ruby 3.1 互換のロジックに修正済みです。)

Rails/OSSパッチ会の会場をDiscordにした

勤務先で行っている今月のパッチ会で、Discord に Rails/OSS パッチ会のサーバーを立てていました。

これまでやりとりに使っていた Idobata の rails ルームが 11 月中旬に閉鎖されることになったためです。Idobata を情報チャンネルとしていた方は Discord への引越しをお願いします (勤務先の開発者ブログでも後日案内します) 。

Discord の招待 URL は以下です。これからパッチ会に参加してみようという方もぜひどうぞ。

discord.gg

Discord を選んだ理由は、テキストチャンネル、音声チャンネル、画面共有と欲しいものが一式揃っていたことと、yahonda さんから Asakusa.rb での活用実績を聞いて良さそうとしたことです (ありがとうございます!) 。Slack と Discord で悩んでいたところでしたが、あまり悩んでいるのに時間を浪費しても仕方ないのでサッと決めて進めました。

次回の Rails/OSS パッチ会は 11月25日(木) 17:00-19:00 です。

Active Record x Oracle 12c (19c) における `ALL_SYNONYMS` のパフォーマンスリグレッション回避

勤務先で行っている今月のパッチ会で、yahonda さんに相談した Active Record Oracle enhanced adapter の発行しているスロークエリ対策について書き残しておきます。先に結論を書くと処理によっては、処理によってはパフォーマンスが3〜5倍ほど改善されました。

問題点を端的に書くと Oracle 12c で ALL_SYNONYMS が遅くなっています (Oracle 19c も同様) 。以下はそのリグレッションについて言及された記事です。

blog.pythian.com

Rails アプリケーションでシノニムを使っていないようであれば、Active Record Oracle enhanced adapter での以下のクエリを削除するモンキーパッチを作成することで改善できます。もしシノニムを使っていたら、、、分かりません。頑張ってください!

さて、ここからはシノニムを使っていない前提で書きます。

Oracle enhanced adapter 6.1.4

以下の範囲のクエリを削除してください。

github.com

Oracle enhanced adapter 6.0.6

対応は Oracle enhanced adapter 6.1.4 と同様です。

github.com

モンキーパッチコード

具体的には config/initializers/oracle.rb のようなファイルとして以下のようなファイルを、アプリケーションに置くことになります。

module ActiveRecord
  module ConnectionAdapters
    # interface independent methods
    module OracleEnhanced
      class Connection #:nodoc:
        def describe(name)
          name = name.to_s
          if name.include?("@")
            raise ArgumentError "db link is not supported"
          else
            default_owner = @owner
          end
          real_name = OracleEnhanced::Quoting.valid_table_name?(name) ? name.upcase : name
          if real_name.include?(".")
            table_owner, table_name = real_name.split(".")
          else
            table_owner, table_name = default_owner, real_name
          end
          sql = <<~SQL.squish
            SELECT owner, table_name, 'TABLE' name_type
            FROM all_tables
            WHERE owner = '#{table_owner}'
              AND table_name = '#{table_name}'
            UNION ALL
            SELECT owner, view_name table_name, 'VIEW' name_type
            FROM all_views
            WHERE owner = '#{table_owner}'
              AND view_name = '#{table_name}'
          SQL
          if result = _select_one(sql)
            case result["name_type"]
            when "SYNONYM"
              describe("#{result['owner'] && "#{result['owner']}."}#{result['table_name']}")
            else
              [result["owner"], result["table_name"]]
            end
          else
            raise OracleEnhanced::ConnectionException, %Q{"DESC #{name}" failed; does it exist?}
          end
        end
      end
    end
  end
end

(Oracle enhanced adapter 内部のいくつかの主要箇所にある) describe メソッドを使っているあらゆる箇所のパフォーマンスリグレッションを回避できる見立てです。開発環境でのスキーマ読み込みに関する例で言えば、このモンキーパッチで bin/rails db:migrate で実行される bin/rails db:schema:dump が3〜5倍くらい高速になりました。

今後の展望

モンキーパッチからの本対応としては、既存の use_old_oracle_visitor のように use_synonyms といった新規設定オプションを用意して、削除しているクエリをバイパスできるようにするのが一案です。

 ActiveSupport.on_load(:active_record) do
   ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
     # true and false will be stored as 'Y' and 'N'
     self.emulate_booleans_from_strings = true

     # start primary key sequences from 1 (and not 10000) and take just one next value in each session
     self.default_sequence_start_value = "1 NOCACHE INCREMENT BY 1"

     # Use old visitor for Oracle 12c database
     self.use_old_oracle_visitor = true

+    # Use `all_synonyms`, true by default.
+    self.use_synonyms = false
+
     # other settings ...
   end
 end

互換性のためデフォルトでは ALL_SYNONYMS をバイパスしない設定にすると思います。

ただ、以下の理由から私の方で本対応への着手をする場合はまだ先の話になりそうです。

  • この日記を書いている時点の rsim/oracle-enhanced リポジトリは master ブランチが落ちているので、まずそこを直す必要がある
  • https://github.com/yahonda/rails-dev-box/tree/runs_oracleOracle enhanced adapter の開発環境が使えなくなったため、新たに構築が必要で時間の捻出が必要 (先が長そう)
  • 新機能になるため、既存の安定版へのバックポーティングがされないと思われることから、急ぎではない (いまなら Rails 7.0 に間に合うかも)

yahonda さんアドバイスありがとうございました!

XP祭り 2021 に登壇した

XP祭り 2021に登壇した。

登壇中 2度の機材トラブルのなかサポートいただいたトラック司会の細澤さん、視聴いただいたみなさんに感謝します。講演序盤で2度の PC ハングに遭遇した際に「Keynote を諦めて PDF で発表する」という頂いたアドバイスが功を奏して、最後まで辿り着けてよかったです (Zoom がハングして 2度の PC 再起動になっていた) 。

Zoom + Keynote でハングしたときは、PDF 出力して Zoom + PDF で配信してみるととりあえず話は続けられるかもしれません。今後の参考まで。

さて、当日のスライドは以下です。

colorbox メンバーと同じプロジェクトでの話を異なる観点で持って伝えているようなコンテンツになったと思うので、収録が公開されたら colrbox メンバーとあわせて見ていただけると面白いかもしれません。

また本編ではあまのりょーさん、坂田さん、水越さん、やっとむさん、米澤さんはじめ、懐かしい顔ぶれで XP コミュニティのホームに帰ってきた感ありました。世の中が落ち着いたら飲みに行きたいですね。

最後に本編に関連する参考書籍を列挙しておきます。実際のところ私が当時読んでいたのは版が古いものですが、本質的な内容自体は大きく改訂されていないことを期待しつつ、より時代に適合しているであろう新しめの版で取り上げています。

XP祭り20周年おめでとうございました。楽しかったです。ありがとうございました!

XP祭り 2021に登壇します

今週末の 2020年9月18日(土) に開催される、XP祭り 2021 で『ソフトウェア見積りの公式』というタイトルで登壇します。

私の登壇は 15:00-15:45 の I Hall です。

https://confengine.com/conferences/xp2021/schedule

eXtreme Programming のカンファレンスということで、eXtreme Programming を背景にしたソフトウェア見積りの話をします。インターネットに繋がっていれば参加できる、無料のオンラインカンファレンスなので、よければ遊びに来てください。

xpjug.connpass.com

また当日は勤務先から8人が登壇します。以下のエントリをあわせてどうぞ。

blog.agile.esm.co.jp

RubyKaigi Takeout 2021に登壇した

RubyKaigi Takeout 2021 に登壇した。

rubykaigi.org

まず最初に。@yahonda さんには、昨年に引き続き事前に英文や構成レビューをしていただきました。多忙のおり、丁寧に見ていただいて本当にありがとうございました。

当日のスライドは以下です。

講演内容について、当初は6つのパートがありセルフレビューでパートをひとつ削って5つのパートで構成していました。その後、@yahonda さんにレビューしてもらった際にストーリ展開の都合でさらにパートをさらにひとつ削ることになり、最終的には講演時の4つのパート構成としたのがリリース版です。フルレンスでの完全版としては世に出すことはないと思うのですが、削ったパート分はどこかで独立したショートトークみたいに使ったりするかもしれません。

登壇について今年は収録と生配信のいずれかを選択できるようになっていましたが、私は昨年の実績ベースで収録という形でのセッションとしました。前年と比べて意識していたのは、事前にタイムテーブルが公開されていたので、収録時にややタイムテーブルを意識したトークを入れたりしてみることで、ライブ感を足せないかというのが小さなひとつの試みとしていました。例えば、他の開発ツールのトークについて少し話題を入れてみたり、裏番組になっていた Jeremy Evans さんが執筆した『Polished Ruby Programming』の中で RuboCop について言及していたので、本編でも触れてみたりといった感じです。本編でも話した RuboCop 導入の戦略のひとつについて Jeremy さんは丁寧に著書の中で説明しているので、そちらもひとつの参考ケースになると思います。

あと、rubocop/rubocop#10000 の PR は間に合ったらトークに含めようと温めていたネタでした (開発者も人間なので、そういった遊び心的なものを持ちながらやっているのが伝わったかな?) 。これの元ネタは kamipo さんによる rails/rails#30000 で、せっかくのキリ番ならコンテンツ性のある PR にしたいというものを形にしたものです。本編で取りあげた rubocop-daemon の統合的なものはさすがに間に合わないので、だいたいのユーザーにとっては益になりそうな、デフォルト並列化を 10000 番ネタにしました。

github.com

登壇が終わっての一番としては、RubyKaigi オーガナイザーチームのみなさんにはトラブル対応の中での配信をして頂き、本当にありがとうございました。また予期しないトラブルの中でも、視聴者のチャット欄は MINASWAN なコメントで間が保たれていて Ruby コミュニティの練度の高さ、自分な好きなコミュニティを再確認できたりしました。

楽しかったです。ありがとうございました & 初日お疲れ様でした!

2021年9月14日追記

さっそく当日の動画が公開されていました。ありがとうございます。

www.youtube.com

f:id:koic:20210909191444p:plain

社内向けに「dRubyによる分散オブジェクトプログラミング勉強会 」を行った

6年くらい前に浜松Ruby会議01でLT登壇した内容を、当時と組織メンバーの顔ぶれも変わっていることもあり社内向けに話した。

www.slideshare.net

現在 @9sako6 がすすめている PofEAA の社内読書会で登場する RPC や RMI, CORBA といった分散システムに関する語彙について、それらがどういったものか道具箱にイメージが増えると良さそうと思ったのがきっかけ。

Ruby で分散オブジェクトプログラミングをする場合には、dRuby というカッコいい標準ライブラリがあるので、そちらを題材のサンプルにしていた。実際のところ dRuby には現職で2度窮地を救われたことがあったので、そのあたりのエピソードを交えつつ話していた (いちおう記しておくと Rails アプリケーション開発での事例ではない) 。

dRuby による分散・Web プログラミングについて詳しく学びたい方は、dRuby 作者の咳さん自身による以下の書籍がおすすめです。

最後にネタ元となる最初の dRuby が記された URL を貼っておきます。

blade.nagaokaut.ac.jp