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



# エラー内容


今朝起きたら、 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を受け取れるように変更する。

```rb:Gemfile

...
gem 'faraday_middleware'
...

```



```rb: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にコンタクトする予定。


こちらは、ほぼ同時刻の被害者の方
https://qiita.com/s_nakamura/items/49b19f2544aa61229bbc


# コメント

> 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
```


# 追記 10/5 12:10 a.m. JST

真の原因は、これかな？（未検証）

https://github.com/elastic/elasticsearch-ruby/issues/457#issuecomment-326204925
`faraday_middleware-aws-signers-v4` は IAM Role から Elasticsearch Service の認証通す middleware

> Can this be the reason why compression is enabled by default ?
> https://github.com/winebarrel/faraday_middleware-aws-signers-v4/blob/588b8c1086a72e2762ea3834bfff3d9f71b66024/lib/faraday_middleware/request/aws_signers_v4.rb#L70
> 
>     if Net::HTTP::HAVE_ZLIB
>       env.request_headers['Accept-Encoding'] ||= 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'
>     end