Scaling Vector Search Infrastructure for AI-Powered Workspace Search

notion · Tech · 2026 · source Agentic: no · promotional bias: low · source-backed · extracted with prompt 0.5.6 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: workspace vector search infrastructure

Notion needed to scale AI Q&A and workspace search across millions of workspaces while cutting vector database and embedding costs, keeping live workspaces fresh, and reducing user-facing search latency.

Constraints:

Architecture

Notion evolved from Spark batch ingestion plus Kafka realtime indexing into a generation-based vector-index strategy, then migrated from dedicated pod clusters to serverless vector infrastructure and turbopuffer, added page-state hashing to avoid needless re-embedding, and moved embeddings generation and serving toward Ray/Anyscale and Ray Serve with open-source embedding models.

Diagram confidence: explicit (topology described by the source).

flowchart LR
  A["Offline Spark indexing path"] --> C["Vector database generations"]
  B["Kafka realtime indexing path"] --> F["Page state hash cache"]
  B --> C
  C --> D["Serverless vector database"]
  D --> E["Turbopuffer search engine"]
  F --> E
  A --> G["Ray and Anyscale embeddings pipeline"]
  G --> H["Ray Serve embedding endpoint"]
  H --> I["Notion AI Q&A and agents"]
  E --> I
  C --> I
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
Offline Spark indexing path Chunks existing documents, generates embeddings, and bulk-loads vectors during onboarding. Vector database generations, Ray and Anyscale embeddings pipeline
Kafka realtime indexing path Processes page edits as they happen to keep live workspaces up to date. Page state hash cache, Vector database generations
Vector database generations Generation-routed index sets that avoided repeated reshard operations during hypergrowth. Offline Spark indexing path, Kafka realtime indexing path, Serverless vector database, Notion AI Q&A and agents
Serverless vector database Decoupled storage and compute to reduce cost and remove capacity bottlenecks. Vector database generations, Turbopuffer search engine
Turbopuffer search engine Object-storage-based search engine used for the multi-billion-object migration and simplified namespace indexing. Serverless vector database, Page state hash cache, Notion AI Q&A and agents
Page state hash cache DynamoDB-backed cache of span text and metadata hashes used to skip unnecessary embedding or reload work. Kafka realtime indexing path, Turbopuffer search engine
Ray and Anyscale embeddings pipeline Managed Ray pipeline for open-source embedding generation with CPU/GPU pipelining. Offline Spark indexing path, Ray Serve embedding endpoint
Ray Serve embedding endpoint Persistent GPU-backed serving layer for query-time embeddings with batching, replication, and autoscaling. Ray and Anyscale embeddings pipeline, Notion AI Q&A and agents
Notion AI Q&A and agents User-facing search and agent features that retrieve workspace and connected-app context. Vector database generations, Turbopuffer search engine, Ray Serve embedding endpoint

Data flow

Existing documents flow through offline Spark jobs that chunk, embed, and bulk-load vectors; live page edits flow through Kafka consumers; generation IDs route workspace reads and writes to the right vector index set; page-state hashes decide whether text embeddings or only metadata patches need updates; Ray/Anyscale pipelines and Ray Serve move embedding generation and query-time embedding serving into GPU-aware managed compute; Notion AI Q&A and agents search the vector store for workspace context.

3 supporting quotes
“Offline path: Batch jobs running on Apache Spark that chunk existing documents, generate embeddings via API, and bulk-load vectors into our vector database”article, stated
“Online path: Real-time updates via Kafka consumers that process individual page edits as they happen”article, stated
“When users or agents search Notion, we need to embed queries on the fly. We can’t search our vector DB until that computation finishes”article, stated

Why this architecture

Notion repeatedly separated scale bottlenecks: generation IDs avoided re-sharding pauses during onboarding, serverless and turbopuffer reduced vector-search cost, span-level hashes avoided wasteful re-embedding, and Ray/Ray Serve consolidated embedding compute while reducing third-party API dependency and query latency.

3 supporting quotes
“This kept us moving without stopping to do any re-shard operations.”article, stated
“This means we can skip embedding altogether, and just issue a PATCH command to our vector database to update the metadata, a much cheaper operation.”article, stated
“Self-hosting embeddings removed a third-party API hop from the critical path, which materially reduced end-to-end latency for user-facing searches.”article, stated

Models used: embedding models · open-source embedding models

Scale

Alternatives considered (roads not taken)

Incremental re-sharding every two weeks

Rejected because: Notion instead provisioned new index generations for new onboarding to avoid repeated reshard operations.

“Re-shard incrementally: Clone the data into another index, delete half, and repeat every two weeks as we onboarded new customers”article, stated
“when a set of indexes approached capacity, we'd provision a new set and direct all new workspace onboarding there.”article, stated

Over-provision to final expected volume

Rejected because: The provider charged for uptime, making over-provisioning prohibitively expensive.

“Re-shard to final expected volume: Our chosen vector database provider charged for database uptime, making over-provisioning prohibitively expensive”article, stated

Re-embed and reload an entire page on any edit

Rejected because: Span text and metadata hashes let Notion update only changed embeddings or patch metadata without embedding.

“Previously, we would have re-embedded and loaded the whole page. Now, we chunk the page, get the previous state of the page from DynamoDB”article, stated

Continue Spark preprocessing plus provider embedding APIs

Rejected because: That created double compute, endpoint reliability dependency, and clunky S3 handoff pipelines.

“"Double Compute" problem. Running Spark on EMR for preprocessing (chunking, transformations, orchestrating API calls), then also paying per-token fees”article, stated
“Embeddings Endpoint Reliability. We were dependent on our provider’s API stability to keep our search indexes fresh”article, stated

Patterns

rag-retrieval (retrieval)

Notion AI answers natural-language questions by searching embedded workspace and connected-tool content through vector search.

“This is essential for Notion AI, which answers natural-language questions by searching content from across a user’s workspace and connected tools”article, stated

batch-offline-processing (data-infrastructure)

Notion used Spark batch jobs for existing-document onboarding and later used Ray to pipeline CPU-bound chunking with GPU-bound embedding generation.

“Offline path: Batch jobs running on Apache Spark that chunk existing documents, generate embeddings via API, and bulk-load vectors into our vector database”article, stated
“Ray natively supports pipelining CPU bound tasks (chunking, detecting page state) with GPU bound embeddings generation within the same node.”article, stated

semantic-caching (cost-control)

The page-state cache stored span text and metadata hashes so unchanged text could reuse existing embeddings and metadata-only edits could patch the vector database.

“To detect changes, we kept track of two hashes per span: one on the span text, and the other on all of the metadata fields.”article, stated
“we can skip embedding altogether, and just issue a PATCH command to our vector database to update the metadata”article, stated

Tradeoffs

Use generation-based index routing during hypergrowth.

Chose provisioning new index generations for new workspaces over incremental re-sharding or over-provisioning to final volume.

Rationale: It kept onboarding moving while avoiding recurring re-shard operations and expensive over-provisioned uptime.

“This kept us moving without stopping to do any re-shard operations.”article, stated

Migrate from pod clusters to serverless vector infrastructure.

Chose storage/compute decoupling with usage-based charging over dedicated hardware pod clusters.

Rationale: Serverless removed storage capacity constraints and cut peak costs, but further optimization was still needed.

“In May 2024, we migrated our entire embeddings workload from the pre-existing dedicated-hardware “pod” architecture to a new serverless architecture that decoupled storage from compute and charged based on usage rather than uptime.”article, stated

Switch to turbopuffer.

Chose object-storage-based search with independent namespaces over continuing the existing vector database architecture.

Rationale: The migration reduced search spend and simplified sharding/generation routing for a multi-billion-object workload.

“After a successful evaluation, we committed to migrating our entire multi billion object workload to turbopuffer in late 2024.”article, stated
“turbopuffer treats each namespace as an independent index without worrying about sharding or generation routing”article, stated

Self-host embedding generation and serving on Ray.

Chose Ray/Anyscale and Ray Serve with open-source embedding models over external embedding APIs and Spark-only preprocessing.

Rationale: Self-hosting removed a third-party API hop and unified CPU/GPU pipeline execution.

“* Model flexibility: Ray lets us run open-source embedding models directly, without being gated by external providers. As new models are released, we can experiment and adopt them immediately”article, stated
“Ray Serve provides most of this out-of-the-box. It allows us to wrap our open-source embedding models in a persistent deployment”article, stated

Gotchas (what bit them)

The original indexes nearly blocked onboarding one month after launch.

Root cause: Demand created a multi-million-workspace waitlist and the initial pod-cluster/index capacity was close to full.

Resolution: Provision new index generations and route new onboarding there instead of stopping for re-shards.

“Just one month after launch, our original indexes were close to capacity. If we ran out of space, we’d be forced to pause onboarding—slowing the rollout of our AI features and delaying value for new users. We faced a classic scaling dilemma:”article, stated
“Each set got a "generation" ID that determined where reads and writes would go. This kept us moving without stopping to do any re-shard operations.”article, stated

Coupling storage and compute inflated vector database cost.

Root cause: Dedicated pod clusters charged for database uptime and made capacity planning expensive.

Resolution: Migrate embeddings workload to serverless storage/compute separation and later turbopuffer.

“dedicated-hardware “pod” architecture to a new serverless architecture that decoupled storage from compute and charged based on usage rather than uptime.”article, stated
“Despite these impressive savings, our annual run rate was still millions per year for vector database costs alone.”article, stated

Page edits caused unnecessary re-embedding and vector reload work.

Root cause: The old indexing path re-chunked, re-embedded, and re-uploaded all spans whenever a page or property changed.

Resolution: Use text and metadata hashes to distinguish embedding changes from metadata-only patches.

“whenever a page or its properties was edited, we would re-chunk, re-embed, and re-upload all of the spans in that page”article, stated
“We detect exactly which spans changed, and only re-embed and re-load those spans into our vector database.”article, stated

The near-real-time embedding pipeline depended on external endpoint reliability and hand-rolled pipelining.

Root cause: Spark preprocessing and provider embedding APIs split compute and required S3 batch handoffs to avoid rate limits.

Resolution: Move embedding generation toward Ray/Anyscale with unified CPU/GPU pipelining and self-hosted open-source embeddings.

“we implemented our own pipelining setup that split the online indexing Spark job into multiple jobs that handed off batches of data via S3.”article, stated
“By consolidating preprocessing and inference onto a single compute layer, we eliminated the double-compute problem”article, stated

Evaluation

Notion evaluated migrations through cost reductions, latency changes, onboarding capacity, data-volume reduction, and provider evaluations before committing to search-engine and embedding-platform changes.

Metrics: daily onboarding capacity · active workspace growth · vector database capacity · search engine spend · AWS EMR compute cost · p50 production query latency · data volume

3 supporting quotes
“we conducted a comprehensive evaluation of alternative search engines, with turbopuffer emerging as a compelling option”article, stated
“The result: * 60 percent cost reduction on search engine spend”article, stated
“Through both of these changes, we achieved a 70% reduction in data volume which saved money on both embeddings API costs and on vector DB write costs.”article, stated

Outcomes

The article spans millions of workspaces, a multi-billion-object vector workload, and user-facing query-time embedding latency.

3 supporting quotes
“By April 2024, we cleared the Q&A waitlist! However, while managing multiple generations of databases got us through the hyper growth stage, it had become operationally complex and expensive. We needed a better architecture.”article, stated
“resulting in several millions of dollars saved annually.”article, stated
“* p50 production query latency improved from 70-100ms to 50-70ms”article, stated