AWS Elasticsearch Serviceが律儀にgzipを返すようになって死んだ [解決済み]

  • 23
    Like
  • 3
    Comment

エラー内容

今朝起きたら、 Elasticsearch + Rails にこんなエラーが起こっていた。

MultiJson::ParseError: unexpected character at line 1, column 1 [parse.c:664]

なんと、いままで Accept-Encoding を無視していた AWS Elasticsearch Service が、急に仕事をしだして、 gzip 形式で返すようになった模様。

追記: なお、 elasticsearch-rails で利用されている faraday は、 Accept-Encoding: gzip がデフォなようです。

バージョン

追記しました

elasticsearch (5.0.0)
elasticsearch-api (5.0.0)
elasticsearch-transport (5.0.0)
elasticsearch-dsl (0.1.4)
elasticsearch-model (0.1.9)
elasticsearch-rails (0.1.9)
elasticsearch-transport (5.0.0)
faraday (0.9.2)
faraday_middleware (0.12.2)
faraday_middleware-aws-signers-v4 (0.1.5)

最新版からは若干古いです。

解決法

faraday_middleware gemを使って、gzipを受け取れるように変更する。

Gemfile

...
gem 'faraday_middleware'
...

config/initializers/aws.rb


Elasticsearch::Model.client = Elasticsearch::Client.new(
  host: ENV.fetch('ELASTICSEARCH_ENDPOINT'),
  port: 80
) do |faraday|
  faraday.use FaradayMiddleware::Gzip # ここを追加
  faraday.request :aws_signers_v4,
                  credentials: Aws::Credentials.new(ENV.fetch('AWS_KEY_ID'), ENV.fetch('AWS_ACCESS_KEY')),
                  service_name: 'es',
                  region: 'ap-northeast-1'
  faraday.adapter Faraday.default_adapter
end

被害者の会

他にも被害者はいる模様。
https://forums.aws.amazon.com/thread.jspa?threadID=223784

Posted on: Sep 26, 2017 10:56 AM

Just my personal experience -
Without any client changes, one day the ES responses stopped working. When I inspected the response, it was gzipped. The client library I was using requested gzip. Since I didn't make any changes to my client code, I am guessing that the following things are true, but none of them are really proven:

a) the client library had always been requesting gzipped responses ( evidence: https://github.com/elastic/elasticsearch-ruby/issues/457#issuecomment-326204925 )
b) the client library was not equipped to receive gzipped responses ( evidence: my code doesn't work )
c) the AWS ES service used to ignore the request for gzipped response, and now it honors it. ( evidence: my code used to work )

I was not able to figure out how to get my client library to stop requesting gzipped responses (seems it would require modifying the faraday library), but by changing my code to look like https://github.com/elastic/elasticsearch-ruby/issues/457#issuecomment-326020618 , I was able to get my client library to understand the gzipped response.

Hope those data points help.

ただ、時系列がずれているので、AWSが順次アップデートをしていったのだろうか?
詳細はAWSにコンタクトする予定。

コメント

gzip対応してないのにAccept-encodingでgzip投げてた利用者側が悪いのでは?

仰る通りです。。
OSSのライブラリを使っている以上、そのライブラリの挙動の責任は利用者にありますね。この記事のタイトルから「AWSやべー」という声が聞こえてきそうですが、どちらかと言うと、「いままでgzip無視しててごめんね、今朝直しといたから」と言った感じですね。

Amazon Elasticsearchのgzipの件、全ノードでgzipが返ってくるわけじゃなくてあるノードでだけgzip返ってくる罠
挙動が戻ってまたエラーになったら...

レスポンスがgzipでない場合、何もせず通過させるので、この変更を入れておけば、仮に挙動がもとに戻っても大丈夫なはずです。

https://github.com/lostisland/faraday_middleware/blob/master/lib/faraday_middleware/gzip.rb#L25

追記 10/4 11:15 p.m. JST

とここまで書いてて気づいたが、 faraday_middleware によると、 Faraday.default_adapter == :net_http を使うと、Gzipは自動解凍されるよう。。。なんだこれ

  # This middleware is NOT necessary when these adapters are used:
  # - net_http on Ruby 1.9+
  # - net_http_persistent on Ruby 2.0+
  # - em_http
462contribution

自分の作ってるサービスでも同じ現象が起こりましたが、同じ対応で修正しました。

ちなみに、以下のように最後の行に追加してしまうと faraday の仕様上動かないので、 do の次の行に入れておく必要がありました。

) do |faraday|
  faraday.request :aws_signers_v4,
                  credentials: Aws::Credentials.new(ENV.fetch('AWS_KEY_ID'), ENV.fetch('AWS_ACCESS_KEY')),
                  service_name: 'es',
                  region: 'ap-northeast-1'
  faraday.adapter Faraday.default_adapter
  faraday.use FaradayMiddleware::Gzip # この行に追加すると動かない
end

詰まりそうな方いらっしゃるかもしれないのでコメントしておきます。

518contribution

searchkick を使っている場合でも同様な現象が発生しました。
対応としても、ほぼ同じですが、変更内容は Searchkick.client を上書きする形でした。

https://github.com/ankane/searchkick/blob/master/lib/searchkick.rb#L51-L67

こちらの記事を見て、大変助かりました。ありがとうございます!

131contribution

同じく、今日問題が起こってました。
東京リージョンでgzipの変更が適応された日が、今日なのでしょうね(告知してからやって欲しかった。。)

挙動が戻ってまたエラーになったら嫌なので、うちは↓の方で解決しました。

headers: { content_type: "application/json", "Accept-Encoding": "" },