Object-storage-native search systems use S3-style object storage as the durable source of truth for search data, while stateless or lightly stateful query nodes cache hot index data on NVMe SSD and in memory. This is different from a conventional search cluster where every shard owns local disks and replication is the primary durability mechanism. The durable index lives in object storage; compute nodes hydrate the pieces they need, serve queries, and can be replaced without moving ownership of the data.
The architecture is attractive for vector search, full-text search, and hybrid retrieval because search corpora are often much larger than their hot working sets. A namespace may contain millions or billions of vectors, but only a subset is queried repeatedly. Keeping the cold corpus in object storage lowers storage cost and simplifies failure recovery, while a cache hierarchy makes warm queries competitive with disk- or memory-resident engines.
The hard part is latency. Object storage has high request latency relative to local disk, so the index format and query planner must minimize roundtrips. Turbopuffer’s public architecture describes this explicitly: data is stored under namespace prefixes, writes append to an object-storage write-ahead log, indexing happens asynchronously, and cold queries are planned around a small number of large object-storage fetches before subsequent queries hit NVMe or memory cache.
The system treats object storage as the authoritative record for both write-ahead log entries and built index artifacts. Query and indexing workers can be stateless with respect to durability: if a node fails, another node can read the same namespace prefix and reconstruct the required state from durable objects.
The durable tier is cheap but slow. A practical design layers NVMe SSD cache and memory cache above object storage. The first query against a cold namespace pays the hydration cost; later queries route to a node that already has the namespace’s index data cached. This makes cache locality a routing concern, not just a storage-engine concern.
Index data must be organized so a cold query can fetch coarse metadata, navigate to a small set of candidate partitions, and then perform a few large reads rather than hundreds of tiny reads. Centroid-based ANN indexes and block-oriented inverted indexes fit object storage better than pointer-heavy structures that require many dependent reads.
Object-storage-native search decouples where data is safely stored from where it is efficiently queried. Object storage provides durable, low-cost state; query nodes provide locality by caching the active working set near compute.
Writes are first appended to a namespace-local write-ahead log in object storage. Once the log object is durable, the system can acknowledge the write. Background indexers then fold log entries into searchable vector, full-text, and metadata indexes. Until indexing catches up, queries can include a slower exhaustive scan over recent log records to preserve strong read-after-write behavior.
// Simplified write path for an object-storage-native search namespace.
func WriteBatch(ctx context.Context, ns Namespace, batch Batch) error {
entry := WalEntry{
Sequence: ns.NextSequence(),
Records: batch.Records,
}
key := fmt.Sprintf("%s/wal/%020d.json", ns.Prefix, entry.Sequence)
if err := objectStore.Put(ctx, key, encode(entry)); err != nil {
return err
}
// At this point the write is durable. Indexing can happen asynchronously.
indexQueue.Enqueue(ns.ID, entry.Sequence)
return nil
}
A load balancer or query router should prefer a node that already has the target namespace cached. If no node is warm, any node can hydrate the needed metadata and index blocks from object storage. The router therefore tracks both ordinary health and search-specific locality: namespace ownership is soft, but cache affinity matters.
Cold queries start by reading compact metadata: index version, partition directory, centroid summaries, block offsets, and filter statistics. The planner then chooses whether to spend another roundtrip to narrow the search or to fetch more candidate data in the current roundtrip. The right answer depends on object size, bandwidth, expected recall, and latency budget.
type ColdPlan struct {
MetadataKeys []string
CandidateBlocks []ObjectRange
IncludeWalTail bool
}
func PlanColdVectorQuery(meta NamespaceMeta, q Vector, topK int) ColdPlan {
centroids := selectNearestCentroids(meta.CentroidIndex, q, topK)
blocks := make([]ObjectRange, 0, len(centroids))
for _, c := range centroids {
blocks = append(blocks, meta.ClusterObjectRange(c))
}
return ColdPlan{
MetadataKeys: []string{meta.DirectoryKey},
CandidateBlocks: coalesceRanges(blocks),
IncludeWalTail: meta.HasUnindexedWrites(),
}
}
Managed vector search platforms often serve many tenants with very uneven activity. Object storage lets cold tenants remain inexpensive while active tenants benefit from cached namespaces. Compute can scale with query load rather than with total stored vector count.
Support tickets, documentation, logs, media metadata, and compliance records can be too large to keep fully replicated on local SSD. An object-storage-native design can keep vector, BM25, and attribute-filter indexes close to the archival data while still supporting first-stage retrieval.
Because durable state is externalized, query workers can be replaced, autoscaled, or moved between regions with less shard-rebalancing work. The tradeoff is that new workers must warm caches before they deliver low-latency service for hot namespaces.
Store each tenant or logical corpus under a stable object-storage prefix with subdirectories for WAL entries, index snapshots, metadata, and compaction manifests. Prefix-level organization simplifies listing, recovery, cache invalidation, retention, and per-namespace access controls.
s3://search-store/{namespace}/
wal/
00000000000000000001.json
00000000000000000002.json
index/
manifest.json
vector-centroids.bin
vector-clusters-00017.bin
bm25-postings-00042.bin
metadata/
filters.bin
stats.json
For vector search, centroid or partition-based layouts can fetch a few large cluster ranges. For full-text search, fixed-size posting blocks with term dictionaries and block-max metadata reduce key lookups. The common principle is to avoid graph traversals or posting scans that depend on many small remote reads.
Strong consistency may require reading recent WAL entries or checking object-storage metadata on every query. Some workloads can accept eventual consistency in exchange for lower warm latency. Expose this as an explicit query contract rather than silently returning stale results.