Crawler: shard-aware seeding for distributed crawling
Add SHARD_ID/SHARD_COUNT env: each worker seeds only domains where hash(domain) % SHARD_COUNT === SHARD_ID (FNV-1a). Defaults to single-node (all domains). Crawler only follows same-domain links, so shards never overlap and lose no coverage vs a single crawler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9d3be71469
commit
7fa8114a7f
1 changed files with 15 additions and 2 deletions
|
|
@ -14,6 +14,18 @@ const MAX_DEPTH = parseInt(process.env.MAX_DEPTH || '2') // link
|
||||||
const MAX_PAGES_PER_DOMAIN = parseInt(process.env.MAX_PAGES_PER_DOMAIN || '30')
|
const MAX_PAGES_PER_DOMAIN = parseInt(process.env.MAX_PAGES_PER_DOMAIN || '30')
|
||||||
const HOST_DELAY_MS = parseInt(process.env.HOST_DELAY_MS || '1000') // politeness per host
|
const HOST_DELAY_MS = parseInt(process.env.HOST_DELAY_MS || '1000') // politeness per host
|
||||||
const FRESH = process.env.FRESH === '1'
|
const FRESH = process.env.FRESH === '1'
|
||||||
|
// Distributed crawling: each worker owns a disjoint slice of the seed domains
|
||||||
|
// (hash(domain) % SHARD_COUNT === SHARD_ID). Defaults = single-node (all domains).
|
||||||
|
const SHARD_ID = parseInt(process.env.SHARD_ID || '0')
|
||||||
|
const SHARD_COUNT = parseInt(process.env.SHARD_COUNT || '1')
|
||||||
|
|
||||||
|
// FNV-1a — cheap, stable hash so every worker agrees on who owns a domain.
|
||||||
|
function hashDomain(s) {
|
||||||
|
let h = 0x811c9dc5
|
||||||
|
for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193) }
|
||||||
|
return h >>> 0
|
||||||
|
}
|
||||||
|
const ownsDomain = domain => SHARD_COUNT <= 1 || hashDomain(domain) % SHARD_COUNT === SHARD_ID
|
||||||
|
|
||||||
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
||||||
const lastHit = new Map() // host -> last fetch ts (politeness)
|
const lastHit = new Map() // host -> last fetch ts (politeness)
|
||||||
|
|
@ -24,12 +36,13 @@ function stats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function seed() {
|
function seed() {
|
||||||
let n = 0
|
let n = 0, skipped = 0
|
||||||
for (const { rank, domain } of readDomains(SEED_DOMAINS)) {
|
for (const { rank, domain } of readDomains(SEED_DOMAINS)) {
|
||||||
|
if (!ownsDomain(regDomain(domain))) { skipped++; continue }
|
||||||
F.enqueue(`https://${domain}`, regDomain(domain), 0, rank)
|
F.enqueue(`https://${domain}`, regDomain(domain), 0, rank)
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
console.log(`Seeded ${n} domains`)
|
console.log(`Seeded ${n} domains (shard ${SHARD_ID}/${SHARD_COUNT}, skipped ${skipped} out-of-shard)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function politeWait(host) {
|
async function politeWait(host) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue