Web Crawler — Cheatsheet¶
One-page revision. Reproduce this from memory and you can drive the interview.
Problem¶
Seed URLs → fetch pages → extract links → follow them → store content. Politely, at scale, refreshed over time.
Profile (say first)¶
Throughput/bandwidth-bound + coordination-heavy — NOT read-latency. Opposite of the URL shortener.
Requirements¶
- Must: fetch, extract+follow, URL dedup, politeness (robots + per-host rate), store content.
- Defer: content dedup detail, freshness, JS rendering, priority, link graph, media.
- Dominant NFRs: max global throughput subject to per-host politeness; robust + resumable.
Numbers (1B pages/month example)¶
| Fetch rate | 1B/mo ÷ 2.5M s ≈ 400/s (peak ~1.2k) |
| Bandwidth | 400 × 100 KB ≈ ~320 Mbps–1 Gbps (first-class cost) |
| Storage | ~100 TB/mo raw → ~20–30 TB/mo compressed → object storage |
| Seen-set | billions of URLs → Bloom filter ~1.2 GB/1B |
| DNS | 400 lookups/s naive → cache it |
Core components¶
Frontier → Fetcher → Parser/Extractor → Content Store
▲ (robots, DNS, per-host rate) │
└──────── new deduped URLs ───────────────────┘
URL Frontier (the heart) — priority + politeness¶
- Front queues = priority (crawl important first).
- Back queues = politeness, one host per queue → ≤1 connection/host.
- Heap of per-host next-eligible times → honor crawl-delay.
- Shard by host → politeness is a local decision (no distributed lock). Durable (never lose it).
Dedup¶
- URLs: Bloom filter (no false negatives; rare FP = skip a new URL) + sharded KV backing. Normalize first.
- Content: exact = content hash; near-dup = SimHash/MinHash.
Politeness (3 parts)¶
- robots.txt — per host, cached w/ TTL; obey Disallow + Crawl-delay.
- Per-host rate — ≤1 in-flight; enforced by one-host-per-back-queue.
- Identify — descriptive User-Agent + contact.
DNS (hidden bottleneck)¶
Cache per host + async resolve + own caching resolvers + serve stale on outage.
Traps & robustness¶
Per-host URL budget + depth limit · param-explosion detection · session-id stripping · max page size · request timeout · redirect cap · tolerant parsing (never crash a worker on bad HTML).
Freshness¶
Adaptive recrawl by change rate + conditional GET (If-Modified-Since/etag → 304 ≈ free) + sitemap lastmod.
Storage¶
Raw pages → object storage (S3), compressed; metadata (url_hash, status, etag, content_hash, body ptr) → KV; links → graph store (offline). No single DB — choose per access pattern.
Bottlenecks (ranked)¶
- Bandwidth/fetch concurrency → more async fetchers
- DNS → cache
- Frontier → shard by host
- Seen-set → Bloom + KV
- Storage → object store + compress
- Politeness = hard ceiling → gain throughput via breadth (many hosts), not depth
Failure one-liners¶
- Fetcher dies → leased URLs redelivered; double-fetch harmless (dedup).
- Frontier dies → scariest (= lost discovery) → durable + replicated per shard; restore from checkpoint.
- Seen-set lost → snapshot Bloom + KV backing; cold = temporary re-fetch spike.
- Slow/hostile server → timeout + size cap + redirect cap; one host can't stall others.
- 429/403 → per-host backoff + Retry-After + park.
- Storage/indexer slow → backpressure to fetchers; decouple via durable queue.
Patterns used (name them)¶
queue+workers (frontier) · idempotency (dedup) · sharding (by host / url-hash) · caching (DNS/robots) · object storage (pages) · rate limiting (politeness) · backpressure.
JS rendering¶
Plain HTML default; targeted headless render tier only where needed (10–100× cost).
Summary line¶
"Throughput/coordination-bound. Two-level frontier (priority front + one-host-per back queues) sharded by host so politeness is local. Stateless async fetchers with cached DNS+robots. Bloom-filter URL dedup + SimHash content dedup. Pages → compressed object storage, metadata → KV. Trap budgets + timeouts for robustness; adaptive conditional-GET recrawls for freshness. Scale by adding fetchers/shards; politeness is the ceiling I design breadth around."
← Back to README