勤務先で行っている今月のパッチ会で、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
module OracleEnhanced
class Connection
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
をバイパスしない設定にすると思います。
ただ、以下の理由から私の方で本対応への着手をする場合はまだ先の話になりそうです。
yahonda さんアドバイスありがとうございました!