feat(page-cluster)!: streaming API for large corpora#917
Merged
Conversation
BREAKING CHANGE: `resolvePageClusterKeys` is now async and takes a factory function `() => Iterable<Page> | AsyncIterable<Page>`. Callers with a materialized array can use the new `resolvePageClusterKeysFromArray` convenience wrapper; existing sync array-based use cases keep working with a one-line change. Corpora at or below `CORPUS_INLINE_THRESHOLD` (20,000 pages) still take the byte-identical in-memory path; larger corpora are processed via three passes: - Pass 0 (HTML-free) — read blocking signals only and compute block keys with the existing blocking / orphan-reassignment / first-party filter pipeline. - Pass 1 (per-block reservoir sampling) — draw up to `BLOCK_SAMPLE_SIZE` (100) pages per block, run Stage A on the sample, emit its CrossBlockUnits, and save learned artifacts (per-block `maxMainDepth`, local-chrome signatures, per-cluster member token sets) for the assignment step. - Pass 1b (streaming assignment) — Jaccard-assign every non-sample page to its block's nearest sample-derived cluster. Additional memory reductions: - `CrossBlockUnit.memberLandmarks: ExtractLandmarksResult[]` becomes `memberLandmarkInstances: PerPageLandmarkInstance[][]`. Landmarks are pre-tokenized at unit creation instead of being re-tokenized by shellQuorum each round. - `mergeCrossBlockClusters` accepts an opt-in `capMembers` option that down-samples merged groups after each merge. Streaming callers pass 100; the in-memory path omits it to keep validated corpora unchanged. Small-corpus regression preserved on the four historically validated corpora (302 / 8,936 / 1,416 / 89 pages → 9 / 21 / 63 / 3 clusters). New streaming path validated on a 176,046-page real crawl: completes in ~28 min at 1.4 GB peak RSS / 2.8 GB peak heap, well within an 8 GB budget. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resolvePageClusterKeysを async ファクトリ入力に変え、コーパスがマシンメモリを超えても完走できるようストリーミング経路を追加。従来の array 入力 API は syntactic sugarresolvePageClusterKeysFromArrayとして保持。BREAKING CHANGE
動機
前 PR (#916) までの実装は全ページを配列に materialize する設計で、10 GB / 176k ページ級の実データで OS SIGKILL / V8 heap OOM が確定。実務の大規模クロールに耐えない。
変更点
経路分岐
CORPUS_INLINE_THRESHOLD(20,000)streaming 経路の 3 パス:
paths/stylesheetHrefs/hostのみ全ページ走査。resolveBlockKeys(新規、既存の blocking / orphan-reassignment / first-party フィルタを合成) で全 blockKey を確定BLOCK_SAMPLE_SIZE(100) ページを Algorithm R でサンプル。サンプルに Stage A を実行し、per-blockmaxMainDepth/ local-chrome signatures / per-cluster member token sets を学習物として保持Stage B (
mergeCrossBlockClusters) はサンプルベースのCrossBlockUnit群に対して従来通り実行。メモリ削減
CrossBlockUnit.memberLandmarks: ExtractLandmarksResult[]→memberLandmarkInstances: PerPageLandmarkInstance[][]。事前トークン化して保持することで per-page ~10× のメモリ削減。Stage B 側 (shellQuorum) も再トークン化を停止mergeCrossBlockClustersに opt-incapMembersを追加。merged group を各 merge 後にダウンサンプリング。streaming 経路のみ有効化、in-memory 経路には影響なし決定論
検証
既存 4 コーパス回帰(in-memory 経路、ゲート値完全一致)
大規模実データ(streaming 経路)
テスト
pass0-blocking.spec.ts、reservoir-sample.spec.ts、resolve-page-cluster-keys-streaming.spec.ts意図的に本 PR に含めていないもの(別 PR で継続)
landmarkGateSimilarityThreshold撤去等)package.jsonのexportsを 4 サブパスに絞る作業onProgress)Test plan
yarn build/yarn lint/yarn testall pass🤖 Generated with Claude Code