🦔

OpenSearch と Amazon OpenSearch Service の安全装置についてお話しします。

2022/12/15に公開

この記事は、AWS Analytics Advent Calendar 2022 の 15 日目の記事になります。
https://qiita.com/advent-calendar/2022/aws-analytics

Why my requests are rejected with error code 429?

OpenSearch や Amazon OpenSearch Service を運用していると、429 エラーでリクエストがスロットルされることがあります。本記事では、429 エラーが発生する背景とその対処法について、歴史的な経緯も交えつつお話しします。

何故安全装置が必要か?

Java 上で実行されている OpenSearch にとって、OOM (Out-Of-Memory) は大敵です。OOM が発生するとプロセスが停止してしまい、クラスターが縮退します。万一連鎖的に複数のノードで OOM が発生した場合、データの保全も危うくなります。このため、OpenSearch では OOM の発生を防止するための策が何重にも張り巡らされています。皆さんが度々遭遇するスロットリングの仕組みも、OOM 防止の一環です。

安全装置いろいろ

安全装置は至る所に仕掛けられており、これらが組み合わさることで OpenSearch Service ドメインに対するリクエストを多層的にチェックしています。クライアントから近いレイヤーから順番に見ていきましょう。

Admission Control (_search, _bulk)

Admission control は、_search, _bulk API に対して動作する Amazon OpenSearch Service 独自のスロットリングの仕組みです。リクエストがドメインエンドポイントに発行された際、ヒープ領域の現在の使用率 (JVMMemoryPressure)、ヒープ領域の最大サイズに対するペイロードサイズの割合を元にスロットルの判断を行います。詳細な仕組みは以下の Blog で説明していますが、下記の条件でスロットルが発生します。

  • インスタンスサイズに対してリクエストペイロードが大きい(10%以上)
  • JVMMemoryPressure が閾値を超えている

https://aws.amazon.com/jp/blogs/big-data/enhance-resiliency-with-admission-control-in-amazon-opensearch-service-successor-to-amazon-elasticsearch-service/

(2023 年 6 月更新) CPU 使用率に基づく Admission Control によるリクエスト拒否も導入されました

https://aws.amazon.com/jp/blogs/big-data/improved-resiliency-with-backpressure-and-admission-control-for-amazon-opensearch-service/

この機構によりスロットルされた場合、後述のキュー溢れとは異なり、シンプルな 429 Too Many Requests を返します。したがって本機構によりスロットルされたことを容易に特定できます。リクエストペイロードサイズの調整、ないしスケールアップでの対処が有効です。

Amazon OpenSearch Service の Auto Tune を有効化することでリクエストペイロードサイズの閾値が緩和される場合もありますが、Auto Tune の判断によっては閾値がむしろ厳しくなる場合もあります。確実な対処はスケールアップによるノードのメモリ、CPU 増強です。

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/handling-errors.html#troubleshooting-throttle-api

Queue Size Limit

Elasticsearch 7.9 より前のバージョンにおける 429 の主な原因です。

OpenSearch は API ごとにスレッドプールを持ち、スレッドを割り当てることでリクエストを処理しますが、割り当て可能なスレッド数には API 毎に上限を設けているため、バーストリクエストに対応するために捌ききれないリクエストをキューに格納する仕組みを取っています。

Backpressure の項でも説明したように、ヒープ領域には限りがあることからキューサイズもまた上限値が設けられており、上限に達した場合は新規のリクエストを拒否します。

キューサイズが上限に達した場合はクライアントに Rejected Exception が返却される他、CloudWatch メトリクス ThreadpoolWriteRejected に記録が残されます。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/opensearch-resolve-429-error/

本エラーが多発する場合は、検索処理であればクラスター全体の CPU 数を増やす、ノードとレプリカを追加するなどのスケールが有効です。スレッドプールの数は CPU 数に依存するため、スレッドを増やすことでキューイングされるリクエストを減らすという対処になります。

インデックス処理であれば、CPU やディスクなどボトルネックになりうる個所の特定と増強、シャード分割による負荷分散、インデックス設定のチューニングなどを検討します。リフレッシュ間隔の調整はリーズナブルで有効性の高い対処法なので、リフレッシュ間隔を調整しつつ次の手を考えるとよいでしょう。

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/bp.html#bp-perf-refresh

Indexing Pressure

Indexing Pressure は、OpenSearch が Elasticsearch から fork する前のバージョンである Elasticsearch 7.9 から導入されたスロットリングの仕組みです。OpenSearch は書き込みリクエストをキューに格納し順次スレッドを割り当てることでスパイクリクエストを捌きますが、キューにリクエストを格納するとその分ヒープ領域の占有量が増加していきます。従来はキューサイズに上限を設けることで、待機中のリクエストによってヒープ領域が過剰に占有されることを防いできましたが、Elasticsearch 7.9 より導入された Indexing Pressure によって、未処理のリクエストによって消費されているヒープ領域の合計容量を追跡し、閾値を超えた場合に新規のリクエストを拒否するようになりました。

https://github.com/elastic/elasticsearch/issues/59263

これにより、キューサイズに余裕があるにもかかわらず OOM が発生するリスクを低減させています。

OpenSearch では Indexing リクエストを Coordinating, Primary, Replica の 3 つのフェーズに分けて処理しており、各フェーズに Indexing Pressure が適用されています。

OpenSearch Service では、CloudWatch メトリクスからどのフェーズで拒否されたかを確認することができます。

https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/managedomains-cloudwatchmetrics.html

注記
バージョン 7.9 では、デフォルトの書き込みキューのサイズが 200 から 10000 に増加したため、このメトリクスは OpenSearch Service からの拒否の唯一の指標ではなくなりました。CoordinatingWriteRejected、PrimaryWriteRejected、および ReplicaWriteRejected メトリクスを使用して、バージョン 7.9 以降での拒否をモニタリングします。

本エラーが発生している場合はヒープ領域の引き上げもそうですが、前述のキューリミットに達した場合と同様、インデックス処理自体のスループット引き上げも重要です。

補足1: Shard indexing Backpressure

上記の Indexing pressure に加えて、OpenSearch では 1.2 より Back Pressure の仕組みをシャードレベルで行うようになりました。よりきめ細やかにリソースの使用状況を追跡することで、特定シャードに負荷が集中することによるスローダウンなどを防止することができます。

https://opensearch.org/blog/shard-indexing-backpressure-in-opensearch/

補足2: Search Backpressure

2022 年 12 月時点では Amazon OpenSearch Service でサポートされていませんが、OpenSearch 2.4 から Backpressure が検索にも適用され、より安定性の向上が図られています。

https://opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/search-backpressure/

https://aws.amazon.com/jp/blogs/big-data/improved-resiliency-with-backpressure-and-admission-control-for-amazon-opensearch-service/

Circult breaker

サーキットブレーカーは OOM を防ぐための最後の安全装置といえます。
サーキットブレーカーは、現状のヒープ領域の使用率、または各キャッシュ領域の使用率ごとに設定された閾値と現在の使用率、新規リクエストによって消費されうるメモリサイズからリクエストを受け付けるかどうか判断します。サーキットブレーカーによりリクエストが拒否された場合、クライアントには Circuit Breaking Exception が返却されます。

"error": { "root_cause": [ { "type": "circuit_breaking_exception", "reason": "[parent] Data too large, data for [<HTTP_request>] would be [16355096754/15.2gb], which is larger than the limit of [16213167308/15gb], real usage: [15283269136/14.2gb], new bytes reserved: [1071827618/1022.1mb]", } ] }

サーキットブレーカーによるリクエスト拒否は多くの API リクエストに対して適用されるため、メモリ不足によりサーキットブレーカーの発動に陥った場合、インデックスやドキュメントの削除によるヒープ領域の開放や、シャード移動を伴うスケールアップによる対処が難しくなるなどの課題があります。

OpenSearch や Elasticsearch が多層的なリクエストチェックと拒否を追加してきたことで、リクエスト拒否の処理判定が直接サーキットブレーカーに落ちてくるケースは減りましたが、こうした機構が存在すること、発動するとどのような影響があるかについては把握しておくことで、クラスターをより安定して運用させることができるようになります。

まとめ

本記事では、クラスターをより安全に運用するために、Amazon OpenSearch Service やコアエンジンの OpenSearch 内の機構について解説いたしました。このナレッジが 429 エラーの切り分けのお役に立てば幸いです。

Discussion