LoginSignup
8
1

Elasticsearch Rails 7とnet-http-persistentを同時に使うときにはFaradayアダプターを指定する

Last updated at Posted at 2024-02-19

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 7net-http-persistent を同時に使うときにはFaradayアダプターを明示的に指定します。

config/initializers/elasticsearch.rb
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

原因はここにありそうです。
ソースコードを見てみましょう。

https://github.com/elastic/elasticsearch-ruby/blob/7.17/elasticsearch-transport/lib/elasticsearch/transport/client.rb#L174-L179

 @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で検索してみます。

https://github.com/elastic/elasticsearch-ruby/blob/7.17/elasticsearch-transport/lib/elasticsearch/transport/client.rb#L362-L375

  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にも現れています。

Gemfile.lock
-      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-transportfaraday-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を確認してみましょう。

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があります。
最新のソースコードを見てみましょう。

https://github.com/elastic/elastic-transport-ruby/blob/2e37d308c22fb053f88997407aec419808ceade7/lib/elastic/transport/client.rb#L315-L328

  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を初期化している箇所をさがします。

config/initializers/elasticsearch.rb
Elasticsearch::Model.client = Elasticsearch::Client.new

があります。
ここで、adapter引数に'default_adapter'を指定します。

config/initializers/elasticsearch.rb
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くらいの差であれば、影響は小さそうです。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1