Railsアプリケーションのgemのバージョンを上げたときエラーが起きた
Railsアプリケーションのgemのバージョンをbundle update
コマンドで上げていました。
bin/rails s
コマンドでRailsアプリケーションを起動するときに次のようなエラーがでるようになりました。
ledsun@MSI:~/pubdictionaries►bin/rails s
=> Booting WEBrick
=> Rails 7.0.8 application starting in development http://localhost:3000
=> Run `bin/rails server --help` for more startup options
Exiting
/home/ledsun/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday/middleware_registry.rb:57:in `lookup_middleware': :net_http_persistent is not registered on Faraday::Adapter (Faraday::Error)
from /home/ledsun/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.9.0/lib/faraday/rack_builder.rb:112:in `adapter'
from /home/ledsun/.rbenv/versions/3.2.1/lib/ruby/3.2.0/forwardable.rb:240:in `adapter'
from /home/ledsun/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/elasticsearch-transport-7.17.10/lib/elasticsearch/transport/client.rb:177:in `block in initialize'
結論 対応方法
先に結論を書きます。
Elasticsearch Rails 7 と net-http-persistent を同時に使うときにはFaradayアダプターを明示的に指定します。
Elasticsearch::Model.client = Elasticsearch::Client.new adapter: 'Faraday::Adapter::NetHttp'
上記のエラーを解消出来ます。
以下は、対応方法を特定するまでの記録です。
対応方法を決める
エラー発生箇所を探す
エラーを読んでみましょう。
:net_http_persistent is not registered on Faraday::Adapter (Faraday::Error)
Faradayに関わるエラーのようです。
もう少し読んでみましょう。
elasticsearch-transport-7.17.10/lib/elasticsearch/transport/client.rb:177
原因はここにありそうです。
ソースコードを見てみましょう。
@arguments[:adapter] ||= __auto_detect_adapter
set_meta_header # from include MetaHeader
@transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
faraday.adapter(@arguments[:adapter])
block&.call faraday
end
ちょっとよくわからないです。
エラーメッセージ中の文言:net_http_persistent
で検索してみます。
def __auto_detect_adapter
case
when defined?(::Patron)
:patron
when defined?(::Typhoeus)
:typhoeus
when defined?(::HTTPClient)
:httpclient
when defined?(::Net::HTTP::Persistent)
:net_http_persistent
else
::Faraday.default_adapter
end
end
とても怪しいソースコードが見つかりました。
when defined?(::Net::HTTP::Persistent)
:net_http_persistent
を通っていそうです。
ところで__auto_detect_adapter
は使われているのでしょか?
先ほど引用したソースコードに
@arguments[:adapter] ||= __auto_detect_adapter
が、ありました。
めちゃくちゃこいつです。
エラーの原因を特定する
ではなぜ?エラーがでるようになったのでしょうか?
bundle update
コマンドを実行したので、Gem関連で何かあるはずです。
Gemfile.lock
のdiffを眺めてみます。
- elasticsearch-transport (7.17.7)
- faraday (~> 1)
+ elasticsearch-transport (7.17.10)
+ faraday (>= 1, < 3)
elasticsearch-transport
が要求する faraday
のバージョンが変わっています。
さらに見ていくと、実際にインストールされるfaradayのバージョンも変わっています。
- faraday (1.10.3)
- faraday-em_http (~> 1.0)
- faraday-em_synchrony (~> 1.0)
- faraday-excon (~> 1.1)
- faraday-httpclient (~> 1.0)
- faraday-multipart (~> 1.0)
- faraday-net_http (~> 1.0)
- faraday-net_http_persistent (~> 1.0)
- faraday-patron (~> 1.0)
- faraday-rack (~> 1.0)
- faraday-retry (~> 1.0)
- ruby2_keywords (>= 0.0.4)
- faraday-em_http (1.0.0)
- faraday-em_synchrony (1.0.0)
- faraday-excon (1.1.0)
- faraday-httpclient (1.0.1)
- faraday-multipart (1.0.4)
- multipart-post (~> 2)
- faraday-net_http (1.0.1)
- faraday-net_http_persistent (1.2.0)
- faraday-patron (1.0.0)
- faraday-rack (1.0.0)
- faraday-retry (1.0.3)
+ faraday (2.9.0)
+ faraday-net_http (>= 2.0, < 3.2)
+ faraday-net_http (3.1.0)
+ net-http
というかfaraday-net_http_persistent
がなくなっています。
- faraday-net_http_persistent (~> 1.0)
エラーメッセージには
:net_http_persistent is not registered on Faraday::Adapter (Faraday::Error)
とありました。
faraday-net_http_persistent
gemがインストールされなくなったことが、エラーの原因のようです。
エラーの原因がわかりました。
次に、どのように対応するのがいいのか考えます。
なぜ、faraday-net_http_persistent
gemはインストールされなくなったのか?
Faraday 2からアダプターの扱いが変わった (Faraday::Adapter::Test::Stubs::NotFound) #Ruby - Qiita
アダプターがFaradayのcoreから外れたので、別途gemのインストールが必要
Faraday 2から、アダプター関連のGEMが自動的にインストールされなくなったようです。
この変更の結果がGemfile.lock
のdiffにも現れています。
- faraday-em_http (~> 1.0)
- faraday-em_synchrony (~> 1.0)
- faraday-excon (~> 1.1)
- faraday-httpclient (~> 1.0)
- faraday-multipart (~> 1.0)
- faraday-net_http (~> 1.0)
- faraday-net_http_persistent (~> 1.0)
- faraday-patron (~> 1.0)
- faraday-rack (~> 1.0)
- faraday-retry (~> 1.0)
Faradayのバージョンを1に下げたら良いのでしょうか?
古いバージョンで固定すると将来のアップデートが大変になりそうです。
もう少し考えてみましょう。
なぜ?elasticsearch-transport
はfaraday-net_http_persistent
を使おうとするのか?
elasticsearch-transpor
は__auto_detect_adapter
メソッドで、使用するFaradayアダプターを選択していました。
__auto_detect_adapter
で:net_http_persistent
が選ばれる条件を見てみましょう。
when defined?(::Net::HTTP::Persistent)
:net_http_persistent
::Net::HTTP::Persistent
定数が定義されているか判定しています。
::Net::HTTP::Persistent
定数はnet_http_persistent
gemで定義される定数です。
Gemfileを確認してみましょう。
gem 'net-http-persistent'
確かにnet_http_persistent
gemを使っています。
net_http_persistent
gemをアンインストールすればいいのでしょうか?
アプリケーションのソースコードを確認してみます。
@analyzer = {
uri: analyzer_url,
http: Net::HTTP::Persistent.new,
post: Net::HTTP::Post.new(analyzer_url.request_uri, 'Content-Type' => 'application/json')
}
実際に使用しています。
net_http_persistent
gemをアンインストールするのは良くなさそうです。
elasticsearch-transport
の判定がおかしいのでは?
Faradayアダプターの有無を判定したいのであれば、::Net::HTTP::Persistent
定数より、Faradayアダプターを表す定数で判定すべきではないでしょうか?
例えば https://github.com/lostisland/faraday-net_http_persistent/blob/10c5b8c30302d13c5217ee1d2c520ca19e699d7f/lib/faraday/net_http_persistent.rb#L16を見ると
Faraday::Adapter.register_middleware(net_http_persistent: Faraday::Adapter::NetHttpPersistent)
Faraday::Adapter::NetHttpPersistent
定数があります。
Faraday::Adapter::NetHttpPersistent
で判定すべきじゃないでしょうか?
今回使用しているelasticsearch-transport
は7です。
実はelasticsearch-transport
には8があります。
最新のソースコードを見てみましょう。
def __auto_detect_adapter
if Gem::Version.new(Faraday::VERSION) >= Gem::Version.new(2)
return :patron if defined?(Faraday::Adapter::Patron)
return :typhoeus if defined?(Faraday::Adapter::Typhoeus)
return :httpclient if defined?(Faraday::Adapter::HTTPClient)
return :net_http_persistent if defined?(Faraday::Adapter::NetHttpPersistent)
else
return :patron if defined?(::Patron)
return :typhoeus if defined?(::Typhoeus)
return :httpclient if defined?(::HTTPClient)
return :net_http_persistent if defined?(::Net::HTTP::Persistent)
end
::Faraday.default_adapter
end
Faradayのバージョンが2かそれ以外かで判定方法をわけています。
elasticsearch-transport
のバージョンを上げたらいいのでしょうか?
elasticsearch-transport
のバージョンは、Elasticsearchのバージョンに対応しています。
elasticsearch-transport
のバージョンを上げるには、Elasticsearchのバージョンも上げる必要があります。
一度に変更する量を減らしたいので、elasticsearch-transport
のバージョンを上げるのも良くなさそうです。
Faradayアダプターを指定する
elasticsearch-transport
のソースコードから__auto_detect_adapter
メソッドを呼んでいる箇所を確認します。
@arguments[:adapter] ||= __auto_detect_adapter
__auto_detect_adapter
を呼ぶ前に@arguments[:adapter]
をチェックしています。
@argument
という名前は引数を表しているように思えます。
と言うことは、外部から使用するFaradayアダプターを指定出来そうです。
アプリケーション中でelasticsearch-trasport
を初期化している箇所をさがします。
Elasticsearch::Model.client = Elasticsearch::Client.new
があります。
ここで、adapter
引数に'default_adapter'
を指定します。
Elasticsearch::Model.client = Elasticsearch::Client.new adapter: 'Faraday::Adapter::NetHttp'
動作確認してみましょう。
ledsun@MSI:~/pubdictionaries►bin/rails s
=> Booting WEBrick
=> Rails 7.0.8 application starting in development http://localhost:3000
=> Run `bin/rails server --help` for more startup options
[2024-02-16 11:11:38] INFO WEBrick 1.8.1
[2024-02-16 11:11:38] INFO ruby 3.2.1 (2023-02-08) [x86_64-linux]
[2024-02-16 11:11:38] INFO WEBrick::HTTPServer#start: pid=2424 port=3000
無事に起動できました。
別解
Gemfileにfaraday-net_http_persistent
を追加する方法はあると思います。
使用するFaradayアダプターが変わるので、アプリケーションの動作に影響が出る可能性はあります。
::Net::HTTP
と::Net::HTTP::Persisitent
くらいの差であれば、影響は小さそうです。