FessのWebクロールでrobots.txtと再取得の動作を改善する

FessのWebクロールで、robots.txtの判定、クロール間隔、過負荷時の再試行、変更のないページの再取得を見直しました。クロール先のルールを適切に扱い、再クロール時の無駄な転送も減らすための変更です。

robots.txtのルール判定

robots.txtをオリジン単位で解決し、AllowとDisallowの最長一致を使って判定します。同じ長さならAllowを優先します。以前のようにAllowをクロール全体のURL許可パターンへ変換する方式をやめ、開始URLにもルールを適用します。

HTTPクライアントとPlaywrightクライアントで共通の判定処理を使うようにしました。ルールで禁止されたURLはINFOログに残し、アクセス失敗URLには登録しません。

robots.txtを取得できない場合

404などの4xxは許可として扱いますが、429、5xx、タイムアウトなどの場合は待機と再試行を行います。標準では最初の失敗後に3回まで再試行し、それでも取得できなければ、そのクロール中は対象オリジンへのアクセスを停止します。

従来の「robots.txtを取得できなければ許可する」挙動から変わる点です。従来の扱いが必要なクロール設定では、パラメーター欄に以下を指定できます。

client.robotsTxtAllowOnUnavailable=true

これはrobots.txtのルールそのものを無効にする設定ではありません。取得失敗も障害として追えるように、後続の修正でアクセス例外を報告し、同じURLの重複報告を抑えています。ルールによる禁止と通信障害を区別して確認できます。

間隔とバックオフ

robots.txtのCrawl-delayをオリジンごとに適用します。小数の値にも対応し、標準の待機上限は60秒です。増分クロールで同じURLに対して行うHEADとGETの間隔を個別に空ける仕組みではなく、URL単位でペースを調整します。

429や503の応答ではRetry-Afterを使い、ない場合は指数的に待機時間を増やして再試行します。既存のクロール設定の間隔と組み合わせて利用できます。

条件付きGETで再転送を減らす

増分クロールでは従来のHEADとLast-Modifiedの比較を残し、それだけで変更を判断できない場合に、保存したETagや更新日時をGETの条件へ渡します。

If-None-Match: <保存済みのETag>
If-Modified-Since: <保存済みの更新日時>

304 Not Modifiedなら本文を再取得・再解析せず、保存済みアンカーを使って後続URLを処理し、有効期限を延長します。対象はHTTP・HTTPSで、ファイル、SMB、FTPやPlaywrightの条件付きGETには適用しません。

クロール設定変更後などに本文を必ず取り直したい場合は、増分クロールを無効にして再取得してください。アップグレード後は、取得できないrobots.txtが原因で停止していないかログを確認するとよいと思います。

関連PR

Docker版FessのOpenSearchで検索遅延を避ける暫定設定

docker-fessのOpenSearch 3.9.0向けCompose定義に、検索処理の低レベルキャンセルを無効にする設定が追加されました。検索中のキャンセル確認に伴う性能低下を避けるための暫定的な対策です。

docker-fess #91を紹介します。Fessの最新リリースとして確認したのは15.8.0で、15.9のリリースを意味するものではありません。以下は変更を含むCompose定義と対応するOpenSearch環境を利用する場合の説明です。

変更された設定

OpenSearchノードのenvironmentに、次の項目が追加されています。

environment:
  - "search.low_level_cancellation=false"

OpenSearch側の初期値はtrueです。今回のCompose定義ではfalseへ変更します。標準のcompose-opensearch3.yaml、vanilla版、multi-instance版、snapshotの5ノード構成が対象です。変更済み定義を使えば項目は含まれています。独自にComposeファイルを管理している場合は、利用する各OpenSearchノードの設定を確認してください。

環境変数として指定する場合、Compose定義を編集しただけでは稼働中のコンテナへ反映されません。通常のCompose更新手順で対象コンテナを再作成して反映します。反映後はOpenSearchのノード設定APIで、各ノードのsearch.low_level_cancellationがfalseになっていることを確認できます。

どのような遅延への対策か

OpenSearch 3.7〜3.9では、Luceneの検索処理を包むキャンセル確認の実装が、一括処理の高速経路や処理オブジェクトの再利用を妨げることが指摘されています。高頻度語のAND・OR件数集計、前方一致、ワイルドカードなど、一致候補を広く走査する検索が影響を受ける場合があります。

背景と修正内容はOpenSearchのIssue #23107とPR #23117で説明されています。利用者の検索条件やデータ量によって影響は異なるため、この設定による高速化の倍率を一律に保証するものではありません。docker-fessのPRで確認されているのは設定の反映であり、Fess環境での性能測定は行われていません。

キャンセルの応答性との引き換え

falseにすると、Lucene内部を走査している最中のキャンセル確認が行われなくなります。キャンセルやタイムアウトした検索は、次の処理フェーズの境界で停止するため、停止までに時間がかかる場合があります。

検索のキャンセルを全面的になくす設定ではありませんが、重い検索をすぐ止めたい運用ではこの違いを考慮してください。検索時間だけでなく、タイムアウト後の処理継続時間や負荷も検証する必要があります。

今回の対策は、修正を含まないOpenSearch 3.9.0に対する回避策です。Composeのコメントでも、上流の修正を含むリリースがイメージへ取り込まれた後に設定を外すよう記載されています。OpenSearchを更新するときは修正の収録状況を確認し、初期値trueへ戻すか再評価してください。

関連PR

Fessの近接検索で単語の距離を指定する

Fessで"word1 word2"~Nという近接検索の指定を、実際の検索クエリに反映するようにしました。フレーズの間に別の単語が入っていても探せるため、完全一致では取りこぼしてしまう文書を見つける際に役立ちます。

フレーズ検索との違い

通常のフレーズ検索は、解析後の単語が連続する条件で検索します。

"quick fox"

近接検索では、フレーズの後ろに~と数値を付けます。

"quick fox"~1

例えば単純に単語ごとの位置が付く場合、quick brown foxのように間に1語入った文も対象になります。タイトルなどフィールドを指定した検索にも使えます。

title:"quick fox"~2

従来はパーサーが距離の値を保持していても、検索エンジンへ渡すmatch_phraseクエリに反映していませんでした。今回、通常フィールド、指定フィールド、未知のフィールドからのフォールバックに距離を渡し、ハイライトのクエリにも反映します。

距離は文字数ではない

Nが数えるのは、フィールドのアナライザーが生成したトークンの位置です。英語ではおおむね単語間の距離になりますが、除去されたストップワードも位置を占めます。単語の順序を入れ替えた一致にも距離が必要です。

日本語では、一般のタイトル・本文フィールドでCJK bigram、言語別フィールドで形態素解析を使うなど、解析方法によって距離の意味が変わります。単純に「N文字以内」と読み替えることはできません。

"全文 検索"~5
"大阪 おいしい"~10

日本語で複数の語の近さを指定する場合も、語を空白で区切ります。つなげた文字列を指定すると、意図した単語間の近接検索にならない場合があります。少し広めの距離から始め、実際の結果を見ながら絞り込んでください。

どんな検索に向いているか

「運用」と「手順」のように関係する語が同じ箇所に現れる文書を探したいときに使えます。AND検索では離れた場所にある語も一致し、完全なフレーズ検索では間に語が入った文書を落としてしまうので、その中間の条件として利用できます。

近接検索の説明ページもFess 15.9のドキュメントに追加しました。実際のアナライザーによる違いを踏まえて距離を調整するのがポイントです。

関連PR