私が関わっている製品で、HerokuのElasticsearchのアドオンはSearchBox(Searchly)を使っているのですが、これでハマったのでちょっと調査した結果を残しておきます。
検索結果画面のページ移動のところで、ページ番号の大きなページにアクセスしようとすると、500エラーが発生していました。メッセージは以下のような感じです。
Result window is too large, from + size must be less than or equal to: [10000] but was [80000]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter
from + sizeが10,000以上だから大きすぎる。scroll apiを使うか、index.max_result_window
の値を大きくしてくれということです。ページング処理自体はkaminari gemに任せていたのですが、件数が多すぎたらこんなことになるとは…。ちなみにこの制限はElasticsearch2から加わったそうです。max_result_windowが大きいと、その分多くのメモリを使ってデータを確保した後に絞込みを行なうことになるので、やたらと大きな数値であることはよくないようです。
とはいえ、変えるだけで済むなら…と思い調べたところ、max_result_windowを設定する方法はすぐに見つかりました。
これをローカルで変更してみたところ、直りました。
curl -XPUT http://localhost:9200/index_name/_settings -d '{ "index" : { "max_result_window" : 1000000 } }'
SearchBoxのほうでも変えようとしたのですが、設定画面がありません。同じ様にcurlでAPIにアクセスしてみたのですが、バリデーションエラーで弾かれました。公式ドキュメントを見た所、settings APIは公開していない模様です。
elasticsearch-modelを使っているので、create_index!をするタイミングでなら設定できるかもしれないと思い、やってみました。 その後、以下のように設定を取得してみました。
curl -XGET http://localhost:9200/index_name/_settings
ローカルでは、max_result_windowが書き換えることができました。
{"index_name":{"setting":{"index":{"max_result_window":1000000,...}}}}
これをSearchBoxに対して実行したら、max_result_window自体がありませんでした。
{"index_name":{"setting":{"index":{...}}}}
ローカルでは成功しましたが、やはりSearchBoxではダメでした。
対処法
対処方法としては、以下の2つが考えられます。
- 上位1万件までを表示対象にする
- 別のアドオンを使う(bonsaiなど)
2の方法は、teratailの質問でそういう回答があったので、おそらくbonsaiだとmax_result_windowの値が変更できるのだと思いますが、検証はしていません。
今回は1の方法で対応するようしました。
まとめ
Elasticsearchはランダムページアクセスに弱いようです。 search_afterを使えば、1万を超えたアクセスはできるようですが、ページング向きではありません。search_afterは、ステートレスなscroll apiのような動きのようです。
とはいえ、ちゃんと絞込みすればいいだけの話なので、そちらに合わせる様にしたほうがよさそうだなと思います。