概要
--list/--list-file(非再帰)クロールで、--dedupe-cap の拒否と自動リトライ(issue #350 )が組み合わさると、リストに含まれない内部ページが is_target=1(フルスクレイプ)として content_items に書き込まれてしまう。
非再帰モードでは、内部リンクは本来 processAnchors(handle-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 がクロール終盤に集中している
想定される原因
非再帰モードで発見された内部リンクは LinkList.add(url, { metadataOnly: true }) で #pending と #metadataOnlyFlag の両方に登録される
--dedupe-cap が有効な場合、同一 URL 形状の判定材料(実際に取得したタイトル/og 情報)が揃うまでは通常通りキューに積まれる。判定材料が揃って閾値超過と判定された時点から、以降の同形状 URL は crawler.ts の enqueue ゲートで拒否される(LinkList.add() 自体が呼ばれない)
何らかの理由(OOM 中断、あるいは自動リトライ機構が pending を再取得するタイミング)で、既に #pending/#metadataOnlyFlag に登録済みだった URL が getCrawlingState() の pending 一覧経由で Crawler#resume() → LinkList.resume(pending, done, options) に渡される
LinkList.resume()(link-list.ts:242-256)は pending 配列の各 URL を this.add(parsedUrl) でオプションなし で再登録している。metadataOnly フラグは DB 側に永続化されていないため、resume 後は復元できない
結果、本来は軽量取得のはずだった 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)の追加を検討する。
概要
--list/--list-file(非再帰)クロールで、--dedupe-capの拒否と自動リトライ(issue #350)が組み合わさると、リストに含まれない内部ページがis_target=1(フルスクレイプ)としてcontent_itemsに書き込まれてしまう。非再帰モードでは、内部リンクは本来
processAnchors(handle-scrape-end.ts)がaddUrl(anchor.href, { metadataOnly: true })として軽量取得(タイトルのみ)扱いでキューに積む。しかし、下記の経路を通るとmetadataOnlyの印が失われ、通常ページと同じフルスクレイプ対象として再投入される。観測結果(実アーカイブでの調査)
--list-fileクロールを実施[auto-retry] 15 pending page(s) remain — waiting Ns before retry N/3というログが出力(issue crawl完了時のarchive.write()がpending件数を無視するため「.nitpickerファイルの存在」が完了を保証しない #350 の自動リトライ機構)/section/news/{連番}.htmlのような、末尾が連番になっているパス)に対して--dedupe-capが発火し、dedupe_cap_eventsテーブルにrejected_count=52の記録が残っているis_target=1(フルスクレイプ済み)として書き込まれていたcontent_items.redirect_dest_idは NULL(リダイレクトではない)first_crawled_at/last_crawled_atがクロール終盤に集中している想定される原因
LinkList.add(url, { metadataOnly: true })で#pendingと#metadataOnlyFlagの両方に登録される--dedupe-capが有効な場合、同一 URL 形状の判定材料(実際に取得したタイトル/og 情報)が揃うまでは通常通りキューに積まれる。判定材料が揃って閾値超過と判定された時点から、以降の同形状 URL はcrawler.tsの enqueue ゲートで拒否される(LinkList.add()自体が呼ばれない)#pending/#metadataOnlyFlagに登録済みだった URL がgetCrawlingState()の pending 一覧経由でCrawler#resume()→LinkList.resume(pending, done, options)に渡されるLinkList.resume()(link-list.ts:242-256)はpending配列の各 URL をthis.add(parsedUrl)でオプションなしで再登録している。metadataOnlyフラグは DB 側に永続化されていないため、resume 後は復元できない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への制限)で表示上は救済されているが、根本の書き込み時点のバグは未修正のまま。対応案(要検討)
metadataOnlyフラグを pending 行として DB に永続化し、resume()時に復元する--dedupe-capで拒否した URL 形状は、pending 行として DB に残さないようにする(enqueue 拒否と DB 上の状態を一致させる)いずれも
LinkList/Crawler#resume()周りの設計変更を伴うため、着手前に再現テスト(--dedupe-cap+ 自動リトライを誘発する E2E)の追加を検討する。