こんにちは。株式会社 LegalOn Technologies でエンジニアをしております、勝田(@WinField95)です。この記事は、情報検索・検索技術 Advent Calendar 2022 の 22日目の記事として執筆されました。LegalForce キャビネについて紹介すると共に、社内で運用されている検索システムを作り直したきっかけとなった課題と改善点についてお話します。
目次
- LegalForce キャビネの検索機能
- Elasticsearch の構成要素について
- 検索システムの課題
- Nodeあたりの最大 Shard 数のソフトリミット
- ソフトリミットの設定値変更による延命措置
- 単一の Index 設計への変更
- まとめ
LegalForce キャビネの検索機能
LegalForce キャビネは、2021年1月に正式版をリリースした契約締結後の契約リスクの制御を目的としたマルチテナンシーSaaSであり、紙で管理されている契約書の電子化、契約書や契約書に記載されている情報を検索可能なデータベースに組み上げる機能を提供しています。2022年11月には、導入社数600社を突破しました!! 🎉
LegalForce キャビネを導入した顧客は、システム上ではテナントとして表され、テナント毎にユーザーや契約書などのリソースの管理がされます。そして、単一のシステムを複数のテナントで共有するアーキテクチャであるマルチテナンシーを採用しています。
契約書に対する操作は REST API としてキャビネ開発チームに提供され、契約書を Elasticsearch の Document として検索システムに登録することで、全文検索を実現します。
Elasticsearch の構成要素について
LegalForce キャビネの検索システムでは、Elasticsearch (注1) を用いています。具体的な問題を説明するための導入として、まずは簡単に Elasticsearch の Node、Cluster、Index、Shard について説明をします。
(注1) 本記事は、Elasticsearch 7.17 をもとに記述されています。
Node
Elasticsearch が動作するサーバーのことを、Node と呼びます。Node は Cluster に含まれ、データを保存し、Cluster のインデキシング機能と検索機能に関係します。
Cluster
協調して動作する Node のグループのことを Cluster と呼びます。Cluster はすべての Node にわたって統合したインデキシング機能と検索機能を提供します。
Index
Elasticsearch 内の Document の集合のような概念です。Elasticsearch の Index には名前が割り当てられ、この名前を参照して、Document に対するインデキシング、検索、更新、削除の操作を実行します。
Shard
Shard は、Elasticsearch の Document の集合を複数の Node で水平分散して管理することで下記の課題を解決します。Elasticsearch の Index は複数の Shard から構成され、Index の作成時に 少なくとも1つの Shard が作成されます。
- Elasticsearch の Index に、単一 Node のディスク容量を超えるデータを格納したい
時系列データやログデータなどの非常に大きなデータセットを扱う場合、ひとつの Node が持つストレージ容量よりも格納したいデータのサイズが大きくなることがあります。複数の Node を用いて Cluster を組み、Shard を各Node に分散配置することで、複数のストレージを前提とした Index の作成ができます。
- 単一 Nodeでは検索リクエストに応えるための処理速度が不足する
Cluster 内に分散配置された Shard を用いて Index を構築することで、クエリ実行時に演算処理を並列化することができます。
検索システムの課題
LegalForce キャビネの検索システム(version1)では、1テナントあたりに1つの Index を割り当てる Index 設計としていました。しかし、1テナントあたりに1つのIndexを割り当てると、テナント数に比例してShardが増えていきます。NodeあたりのShardが増えすぎると、以下の問題が発生します。
- Node あたりの 最大 Shard 数のソフトリミットを超える可能性がある。新規 Node の追加やソフトリミットを変更しない場合、新規テナントの追加が出来なくなる。
- オーバーシャーディングのリスクがある。Cluster 内に Shard が多すぎる状態が、オーバーシャーディングであり、検索の応答効率の低下や Cluster が不安定になる可能性がある。
これら問題を解消するために、テナント数によらず、Nodeあたりの Shard の数を一定以下に保つ Index 設計が必要となりました。そこで、すべてのテナントに関する Elasticsearch の Document を1つの Index で管理するように Index 設計を見直し、検索システム(version2) の開発に取り組みました。
Nodeあたりの最大 Shard 数のソフトリミット
Elasticsearch の Node が持つことができる 最大 Shard 数のソフトリミットのデフォルト値が1000になります。(*1) 例えば、1つの Node で構成される Clusterで、1000社以上のテナントが存在する場合、テナントが独自に持つIndexごとに少なくとも1つの Shard が生成される設計であるため、Shard 数が1000を超えてしまうことになります。
Shard 数がソフトリミットを迎えた場合、Elasticsearch の Index の作成を試みたタイミングでエラーが生じるようになります。例えば、下記のようなエラーが生じ、Elasticsearch の Index 作成に失敗します。このエラーでは、Shard 数の上限値である1000に対し、991個の Shard が存在している前提のもとで、そこから10個の Shard を確保しようとして Index の作成に失敗しています。
Validation Failed: 1: this action would add [10] total shards, but this cluster currently has [991]/[1000] maximum shards open;
ソフトリミットの設定値変更による延命措置
検索システム(version2) の開発期間中も、現行の検索システム(version1)でサービスを運用し続ける必要があります。検索システム(version2) のリリース前に、新規テナントが現行の検索システムに追加不可能になる事態は避けなければなりません。
このような事態を防ぐため、Node あたりの 最大 Shard 数のソフトリミットを1000から2000に上げる調整をすることで時間稼ぎをしました。また、Cluster への Node 追加による延命措置の手段もありますが、運用コストの節約のため、ソフトリミットの設定値変更という手段を取りました。導入社数の増加ペースや営業部隊の増員計画などから、変更後の設定値については、リリース時に導入社数が1000社以上になる可能性はあるが、2000社以内に収まることが予想できていました。このため、運用面で安全と思われる値として2000を設定することになりました。
Elasticsearch の cluster.max_shards_per_node の値を cluster update settings API を用いて変えることで、Node あたりの 最大 Shard 数のソフトリミットを変更することができます。
ただし、デフォルト値を大きく超えるような値を設定することは、多くの Shard を生成することに繋がります。多量の Shard の存在は、オーバーヘッドの増加に繋がるため注意が必要です。(*2) また、 長期的な解決策として Cluster への Node の追加や、Shard 数を減らすことが推奨されています。(*3)
単一の Index 設計への変更
前述した課題の解決のため、検索システム(version2) では、テナント数によらず、Node あたりの Shard の数を一定以下に保つことを目的とし、テナントごとに Elasticsearch の Index を生成する検索システム(version1)の設計から、すべてのテナントに関する Elasticsearch の Document を1つの Index で管理する設計に変更しました。
LegalForceキャビネにおける、テナントごとに Elasticsearch の Index を生成する設計では、テナント毎に格納されるドキュメント数は異なり、小さなデータを持つ Shard から、大きなデータを持つ Shard が存在します。下記の図のように、各テナントの Elasticsearch の Index が持つ Primary Shard のデータ量を大きい順にプロットしてみると、ロングテールな曲線が得られます。これより、少数のテナントが大きなデータを持ち、多くのテナントが比較的小さなデータを持つような傾向が分かります。8割ほどの Shard が 300MB 以下のデータサイズであり、小さなデータサイズの Shard が多数存在していました。
一方、単一 Index の設計では、Shardの総数を制御できます。例えば、検索システム(version1) では、Node あたり600個ほどの Shard を確保していましたが、検索システム(version2) では、160個の Shard で済むようになりました。Elasticsearch 7.17 では、 Java Heap Size 1GB あたり、20個以下の Shard を確保することが推奨されており(*4)(注2)、Java Heap Size が 8GB である Node を使用しているため、Node あたり 160個の Shard を確保しています。
(注2) Elasticsearch 8.3 より、「ヒープメモリ1GBあたり20個以下の Shard の確保」を目安とする方針は非推奨となりました。Shard あたりのヒープ使用量が大幅に減少したためです。新しい方針は、文献(*2)に記載されていますので、興味のある方は確認してみてください。
なお、Elasticsearch の Document へのアクセスは、テナントごとに定義されている識別子での絞り込みを常に行うことで、異なるテナントの Document へのアクセスを防止するように検索システム(version2) を設計しています。さらに、Document の保存と検索時に routing を指定することで、テナントごとに用いる Shard を限定するようにしています。これにより、多数のShardを横断して検索するオーバーヘッドを避け,検索を高速化するメリットが得られます。(*5)
まとめ
今回は、検索システム(version2)の開発に至った課題と改善点について紹介しました。もとはテナントごとに Elasticsearch の Index を作成する設計としていましたが、小さなデータサイズの Shard が多く存在することで、オーバーシャーディングのリスクや、新規テナント追加機能の不全という運用上のリスクが生じました。これら課題に対し、単一の Index に対して複数のテナントの Elasticsearch の Document を管理する設計への変更を行い、Shard の数を固定することにより、運用上の課題を回避しました。今後、マルチテナンシーを前提とした検索システムの設計や、リプレイスをするときの参考になれば幸いです。
メンバー募集中!!
株式会社 LegalOn Technologies では、SREや、バックエンドエンジニア、検索システムの開発に興味のあるインターン生を募集しています。気軽にご応募ください!
ZST-03 Engineering Internship, Search
TECH-201- SRE / Site Reliability Engineer
TECH-101- Software Engineer, Back-end, LegalForce
TECH-102- Software Engineer, Back-end, LegalForce Cabinet
TECH-MI-102- Software Engineer, LegalOn Technologies Research
参考:
(*1) Elastic Docs> Elasticsearch Guide [7.17]> Set up Elasticsearch> Configuring Elasticsearch, Cluster-level shard allocation and routing settings, cluster-max-shards-per-node
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-cluster.html#cluster-max-shards-per-node
(*2) How many shards should I have in my Elasticsearch cluster
https://www.elastic.co/jp/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster
(*3) Elastic Docs > Elasticsearch Guide [7.17] > How to, Size your shards, Troubleshoot shard-related errors
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/size-your-shards.html#troubleshoot-shard-related-errors
(*4) Elastic Docs > Elasticsearch Guide [7.17] > How to Size your shards, Aim for 20 shards or fewer per GB of heap memory
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/size-your-shards.html#shard-count-recommendation
(*5) Elastic Docs > Elasticsearch Guide [7.17] > Search your data, Search shard routing
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-shard-routing.html