Skip to content

--list クロールで dedupe-cap 拒否 + auto-retry 後にリスト外ページがフルスクレイプされる #369

Description

@YusukeHirao

概要

--list/--list-file(非再帰)クロールで、--dedupe-cap の拒否と自動リトライ(issue #350)が組み合わさると、リストに含まれない内部ページが is_target=1(フルスクレイプ)として content_items に書き込まれてしまう。

非再帰モードでは、内部リンクは本来 processAnchorshandle-scrape-end.ts)が addUrl(anchor.href, { metadataOnly: true }) として軽量取得(タイトルのみ)扱いでキューに積む。しかし、下記の経路を通ると metadataOnly の印が失われ、通常ページと同じフルスクレイプ対象として再投入される。

観測結果(実アーカイブでの調査)

  • 1,808 件の URL リストで --list-file クロールを実施
  • クロール終盤に [auto-retry] 15 pending page(s) remain — waiting Ns before retry N/3 というログが出力(issue crawl完了時のarchive.write()がpending件数を無視するため「.nitpickerファイルの存在」が完了を保証しない #350 の自動リトライ機構)
  • 同一 URL 形状(例: /section/news/{連番}.html のような、末尾が連番になっているパス)に対して --dedupe-cap が発火し、dedupe_cap_events テーブルに rejected_count=52 の記録が残っている
  • 上記と同じ URL 形状に属する 14 件の URL が、クロール終盤の約 25 秒間に is_target=1(フルスクレイプ済み)として書き込まれていた
    • リストには含まれていない URL
    • content_items.redirect_dest_id は NULL(リダイレクトではない)
    • first_crawled_at/last_crawled_at がクロール終盤に集中している

想定される原因

  1. 非再帰モードで発見された内部リンクは LinkList.add(url, { metadataOnly: true })#pending#metadataOnlyFlag の両方に登録される
  2. --dedupe-cap が有効な場合、同一 URL 形状の判定材料(実際に取得したタイトル/og 情報)が揃うまでは通常通りキューに積まれる。判定材料が揃って閾値超過と判定された時点から、以降の同形状 URL は crawler.ts の enqueue ゲートで拒否される(LinkList.add() 自体が呼ばれない)
  3. 何らかの理由(OOM 中断、あるいは自動リトライ機構が pending を再取得するタイミング)で、既に #pending/#metadataOnlyFlag に登録済みだった URL が getCrawlingState() の pending 一覧経由で Crawler#resume()LinkList.resume(pending, done, options) に渡される
  4. LinkList.resume()link-list.ts:242-256)は pending 配列の各 URL を this.add(parsedUrl)オプションなしで再登録している。metadataOnly フラグは DB 側に永続化されていないため、resume 後は復元できない
  5. 結果、本来は軽量取得のはずだった URL が通常のフルスクレイプとして処理され、is_target=1 で書き込まれる

--dedupe-cap のバースト特性(issue #208/#293 相当、判定材料が育つまでの構造的遅延)と、LinkList.resume()metadataOnly を永続化・復元しないことの組み合わせが根本原因と推測される。後者は --dedupe-cap を使わない通常の中断・再開でも同様に発生しうる(pending 行がフルスクレイプ対象扱いになる)はずだが、今回は --dedupe-cap によるバーストで顕在化件数が観測できた。

影響

--list/--list-file で明示的に指定していないページが、content_items に通常ページとして紛れ込む。read model(viewer_pages)側では別途 fromList アーカイブのスコープ絞り込み(config.roots への制限)で表示上は救済されているが、根本の書き込み時点のバグは未修正のまま。

対応案(要検討)

  • A. metadataOnly フラグを pending 行として DB に永続化し、resume() 時に復元する
  • B. --dedupe-cap で拒否した URL 形状は、pending 行として DB に残さないようにする(enqueue 拒否と DB 上の状態を一致させる)

いずれも LinkList/Crawler#resume() 周りの設計変更を伴うため、着手前に再現テスト(--dedupe-cap + 自動リトライを誘発する E2E)の追加を検討する。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions