Rails
Elasticsearch
AmazonElasticsearchService

朝起きたらAmazon Elasticsearchから取得した検索結果でJson Parse Errorが起きた件

前日まで普通にAmazonElasticsearchの検索が使えたのに、朝急に

MultiJson::ParseError: 822: unexpected token at '�'

というエラーが頻発したので、その対処方法の共有です。

環境

  • Amazon Elasticsearch
  • Elasticsearch(ver2系、5系)
  • elasticsearch-rails-5.0.4

事象

Elasticsearchから返ってきたjsonをparseしてエラー。
何が起きたのか?
以下確認手順を記載。

調査

  • エラー発生箇所に「binding.pry」を入れてそもそもどんなjsonデータをparseしようとしてエラーになっているのか調査 bundle open elasticsearch-transportでエラーが起きていた箇所を確認。pryを使ってjsonの中身を確認
elasticsearch-transport-5.0.4/lib/elasticsearch/transport/transport/serializer/multi_json.rb
module Elasticsearch
  module Transport
    module Transport
      module Serializer

        # An abstract class for implementing serializer implementations
        #
        module Base
          # @param transport [Object] The instance of transport which uses this serializer
          #
          def initialize(transport=nil)
            @transport = transport
          end
        end

        # A default JSON serializer (using [MultiJSON](http://rubygems.org/gems/multi_json))
        #
        class MultiJson
          include Base

          # De-serialize a Hash from JSON string
          #
          def load(string, options={}) 
            ::MultiJson.load(string, options) <<<<< ここのstringの中身を確認
          end

          # Serialize a Hash to JSON string
          #
          def dump(object, options={})
            ::MultiJson.dump(object, options)
          end
        end
      end
    end
  end
end

  • 中身をみるとjsonデータでなく、何かエンコーディングされたような形式ものになっていた
  • なぜそうなったのか・・・。AmazonElasticsearchの仕様変更でgzip形式返すようになった。

対応

Elasticsearchの接続を行っていた箇所に以下のような修正を追加

      Elasticsearch::Client.new(
        hosts: hosts,
        randomize_hosts: true,
        request_timeout: 10,
        reload_connections: false,
        sniffer_timeout: 3,
        reload_on_failure: false,
        transport_options: { headers: { 
                                                             content_type: 'application/json',
                                                           "Accept-Encoding": "" <<<<< ここを追加
                                                          }
                                                     }, 
        log: true
      ) do |f|
        f.request :aws_signers_v4,
                   credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']),
                   service_name: 'es',
                   region: 'ap-northeast-1'
        f.adapter Faraday.default_adapter
      end

こちらを参考にしました。

<追記> 自分が忘れないようにするためのメモ: elasticsearch-railsの方でHttp通信で使っているfaradayは「Accept-Encoding: gzip」 がデフォルトの設定らしい。