Indexing
Assume ~2 KB/result.
If we return 10 results:
2 KB × 10
≈ 20 KB/search
At 15K QPS:
20 KB × 15K
≈ 300 MB/sec
Suppose we have:
100 index shards
Each search query fans out to all 100 shards:
15K search QPS
×
100 shards
1.5M shard requests/sec
This is an important number because our index infrastructure actually handles ~1.5M shard-level requests/sec, not merely 15K.
Each shard should therefore have multiple replicas.
For example:
100 logical shards
×
3 replicas
300 physical index replicas
The retrieval layer sends each query to one healthy replica per shard, so normal traffic is still approximately:
15K × 100 = 1.5M shard RPC/sec
Suppose the final compressed index is roughly 20% of raw content size as a rough planning assumption:
500 TB × 20%
≈ 100 TB
Then with 3 replicas:
100 TB × 3
≈ 300 TB
Suppose we want newly crawled documents searchable within 5 minutes.
If we need to process:
10M new/updated pages/hour
that's:
10M / 3600
≈ 2,800 documents/sec
Only one main api
Search API
GET /v1/search?q=best+toronto+restaurants&limit=10&cursor=
response:
{
results[] => arrray of document data : title, url, snippet,
nextCursor => opaque cursor
}
The cursor is opaque to the client but conceptually contains:
queryHash
indexVersion
lastScore
lastDocumentId
Results are deterministically ordered:
score DESC, documentId ASC
This allows the next request to continue after the previous result.
For our system we have two seperate architect, one handling the search, the other handling crawl and indexing pages
API Gateway
Load Balancer
Query Service
Query Cache
Caches popular query results.
query + rankingVersion
↓
cache
Cache failure isn't fatal; requests fall through to retrieval.
Retrieval Service
Distributed Index - Shards
hash(docID) → shard
Ranking Service
Crawl/Indexing Architect
Crawler workers:
Kafka
Indexing pipeline:
We dont have traditional relational database for this system, rather we have:
Object storage like S3 -> Document storage
KV store like Cassandra -> Document crawl metdata
doc_id {
url,
content_hash,
langauge
last_crawled_at,
next_crawl_time,
crawl_status,
metadata
}
Distributed inverted index
"restuarnats" : [doc1, doc2]
"toronto" [doc2, doc3, doc4]
Now we will deep dive into the following topics:
How would querying work when using multiple shards
Replication
Index segments
New documents
│
▼
Build Segment
│
▼
Publish Segment
Shard 1 -> Segment A
Segment B
Segment C
Periodically
A + B + C -> merged to become segment D
If use segments then what about updated document?
BM25/Lexical Scoring
Shards -> Find matching doc -> Apply BM25/Lexical score -> return top K documents
Additional ranking:
ML Ranking
Failure Handling
Why multi-stage ranking i.e, cheap first, expensive later?