patorashのブログ

方向性はまだない

Elasticsearchを5.6から7.9にバージョンアップ

過去にElasticsearchのバージョンアップをしたのだけれど、そのバージョンも既にEOLとなり、早く最新のバージョンにしたいと思いつつも、なかなかできずにいた。 ようやくできるタイミングがきたので、取り組んでみた。まだステージング環境では検証が終わっていないけれど、CIが通ったので修正点を一旦まとめる。

やったこと

Elasticsearch 7.9.1のDockerイメージを作成

kuromojiプラグインを入れたDockerイメージをDockerHubに作った。

https://hub.docker.com/repository/docker/patorash/elasticsearch-kuromoji

docker-compose.ymlを修正

Elasticsearchの公式のページにあるdocker-composeの設定を参考にした。

www.elastic.co

しかし、CircleCIでテストをする際にdocker-composeで行っているのだが、その際にElasticsearchのコンテナが起動しなかった。 起動しなかったときのエラーはこれ。

elasticsearch_1 | ERROR: [1] bootstrap checks failed elasticsearch_1 | [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

max_map_countの値が低すぎるから増やせという話だけれど、CircleCIのVMカーネルの値は変更できないということだった。

Running Elasticsearch 5 - Build Environment - CircleCI Discuss

このフォーラムで紹介されていた設定を追加した。environmentのところの"transport.host=localhost""bootstrap.system_call_filter=false"だ。 これで、CircleCIのdocker-composeでElasticsearchが起動するようになった。

version: '3.3'

services:
  elasticsearch:
    image: patorash/elasticsearch-kuromoji:7.9.1
    expose:
      - "9200"
      - "9300"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - cluster.initial_master_nodes=es01
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "TZ=/usr/share/zoneinfo/Asia/Tokyo"
      - "transport.host=localhost"
      - "bootstrap.system_call_filter=false"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    healthcheck:
      test: ["CMD", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
      interval: 60s
      timeout: 30s
      retries: 3

関連gemの更新

  • elasticsearch-rails
  • elasitcsearch-model

この2つを更新した。

コードの修正

クラウドワークスさんのところの記事を参考にした。

engineer.crowdworks.jp

ここに書かれていた通り、_allが使えなくなったので削除したのと、copy_toを使うようにした。copy_toは6系で追加されたらしい。1つのキーワード検索用のフィールドを作り、そこに対して各フィールドをcopy_toで集める。今までは検索対象のフィールドをいくつも指定していたため、効率が悪かったけれど、これを使うことで1つのフィールドだけを検索対象とできるので効率がよくなるそう。

あとは、ngramの設定を変更した。以前はmin_gramを1に、max_gramを20に指定していたのだが、起動時にエラーとなった。max_ngram_diffを19に設定する必要があった(デフォルトで1のため)。

www.elastic.co

これで大体のテストは通ったのだが、ソートのテストがうまくいかなかった。

ソート順がElasticsearchへの指定順にならない

なぜかソート順がおかしいのでgemのコードを調べたところ、Elasticsearchから取得したデータをActiveRecordにするところでorderが定義されているとElasticsearchのソート条件よりもActiveRecordのソート条件のほうが優先されるように変更されていた…。

github.com

default_scopeでorderを定義していたため、elasticsearch-railsrecordsメソッドを呼んだ時点でもうダメだった。default_scopeでのorderを止めるのは変更点が多すぎて厳しいので、elasticsearch-railsから呼ばれた場合のみ、default_scopeを無効にしたいと考え、caller_locationsを使って対処した。

caller_locationsは処理が呼び出された場所を取得するので、そのパス情報の中にelasticsearch/model/adapters/active_record.rbが含まれていたらorderを無視するようにした。ただ毎回この処理が実行されるわけなので、オーバーヘッドがどれくらいになるのかが気になる…が、今はこれで。これで、Elasticsearchのソート順でデータが取れるようになった。

default_scope do
  paths = caller_locations.map(&:path)
  order(name: :asc) unless paths.any? { |path| path.end_with?('elasticsearch/model/adapters/active_record.rb') }
end

とりあえず、これでCIのテストが通るようになった👍

5にバージョンアップしたときと比べると比較的楽にバージョンアップできそう。