過去に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の設定を参考にした。
しかし、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つを更新した。
コードの修正
クラウドワークスさんのところの記事を参考にした。
ここに書かれていた通り、_all
が使えなくなったので削除したのと、copy_to
を使うようにした。copy_to
は6系で追加されたらしい。1つのキーワード検索用のフィールドを作り、そこに対して各フィールドをcopy_to
で集める。今までは検索対象のフィールドをいくつも指定していたため、効率が悪かったけれど、これを使うことで1つのフィールドだけを検索対象とできるので効率がよくなるそう。
あとは、ngramの設定を変更した。以前はmin_gram
を1に、max_gram
を20に指定していたのだが、起動時にエラーとなった。max_ngram_diff
を19に設定する必要があった(デフォルトで1のため)。
これで大体のテストは通ったのだが、ソートのテストがうまくいかなかった。
ソート順がElasticsearchへの指定順にならない
なぜかソート順がおかしいのでgemのコードを調べたところ、Elasticsearchから取得したデータをActiveRecordにするところでorderが定義されているとElasticsearchのソート条件よりもActiveRecordのソート条件のほうが優先されるように変更されていた…。
default_scopeでorderを定義していたため、elasticsearch-railsのrecords
メソッドを呼んだ時点でもうダメだった。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にバージョンアップしたときと比べると比較的楽にバージョンアップできそう。