Why is the first vector search query on S3 slow?
The first query is slow because nothing is cached yet: the search service has to fetch index data from the bucket before it can rank anything, and each fetch from S3 or GCS costs tens of milliseconds before a byte arrives. How slow it gets depends on how many sequential round trips the index layout needs and how many bytes each query pulls. In our own runs at 1 million 768-dimension vectors, a first query that read whole index partitions pulled 391 MB from the store, while a layout built for ranged reads answered the same query with about 3.1 MB in four round trips. Once data is cached, the same queries run in tens of milliseconds.
Every number on this page comes from runs we measured on Mixpeek's vector store (MVS) between July and October 2026, or from a vendor page we link. Each row says where it ran. None of it ran on a customer's production data.
What happens during a cold query on object storage?
A vector index on object storage keeps its data as files in a bucket. A query node with an empty cache has to read some of those files before it can answer. For a centroid-based index (IVF, the usual layout for object storage) the reads come in sequence:
1. Read the namespace's small header or manifest. 2. Read the centroids and pick the partitions closest to the query vector (nprobe of them). 3. Read the compressed vector codes and ids for those partitions, in parallel. 4. Read the full-precision vectors of the best candidates to rerank them, in parallel.
Each step waits for the one before it. So the store's share of a cold query is roughly the number of sequential round trips times the latency of one GET, plus however long it takes to download the bytes. A graph index such as HNSW needs many more sequential hops per query, which is why object-storage systems use centroid layouts. turbopuffer's architecture notes make the same point.
How many bytes does a cold query read?
This is where layouts differ most. We measured a 1M x 768 fixture on a local MinIO store with 20 ms of injected latency each way, so a small GET took 40 to 56 ms, about one same-region GCS read. The shard started with an empty disk each round.
| Step (1M x 768, cold) | Requests | Bytes read |
| Shard boot (index, manifest, payloads) | 18 GET, 17 HEAD, 10 LIST | 76 MB |
| First dense query, whole partitions | 17 GET | 391 MB |
| First 1% filtered query, every partition | 66 GET | 1,580 MB |
| Range-read prototype, same dense query | 4 round trips | about 3.1 MB |
How fast is vector search on S3 once the data is cached?
Fast enough for interactive search. Cold, warm and hot latency are three separate numbers, so ask any vendor for all three.
| Measurement | Result | Where it ran |
| Cold, 1M x 768, from true zero through the API | p50 0.697 s, p99 1.311 s | Proof stack on GKE with MinIO, October 2026 |
| Recall@10, 1M x 768, 1,000 held-out queries | 0.9759 against exact cosine ground truth | Same stack |
| Resident shard, 2M x 768, unfiltered | p50 50.28 ms, p95 58.79 ms, p99 63.13 ms | Staging, synthetic data, July 2026 |
| Resident shard, 2M x 768, filter at 0.2 selectivity | p95 59.11 ms, recall@10 1.0 (0.9996 unfiltered) | Same, nprobe 16 of 128 partitions |
| Cold reads from GCS, 80.6M vectors, compressed codes only | p50 941 ms, p99 1,352 ms | Staging, July 2026 |
| Write acknowledged in GCS, 1 writer | p50 107.9 ms, p95 140.5 ms, p99 162.2 ms | Staging on real GCS, October 2026 |
| Write acknowledged in GCS, 8 writers | p95 176.6 ms | Same |
Why does recall drop on large indexes stored in S3?
Recall drops when the number of partitions grows and the number searched does not. At 80.6M vectors in 5,724 partitions, our default probe width gave recall@10 of 0.355. Raising nprobe to 1,024 brought it to 0.960, at a warm p50 of 808 ms. On object storage every extra partition probed is more bytes and possibly another round trip, so teams quietly keep nprobe low and lose recall without noticing. Measure recall against an exact search on a sample of your own queries at your real size, and rerun it whenever the corpus grows by a large factor.
Filters are the other recall trap. In our 1M x 768 runs, filters matching 0.1% and 1% of the data matched an exact scan on 100 and 99 of 100 queries. At 50% selectivity recall fell to 0.815, which is still open work on our side. A filter applied after the vector search can return fewer than k results or the wrong ones. Our guide to pre-, post- and in-place filtering covers the options.
How do you make the first query faster?
When should you not put the index on object storage?
When every query must answer in under about 10 ms, including the first one, as in ad ranking or autocomplete. When writes must be visible in a few milliseconds at high rates: our measured acknowledgement for a durable write to GCS is 140 to 177 ms at p95, because each write waits for the bucket. For most search over files, archives and media, a cold first query of about a second and warm queries in tens of milliseconds are acceptable, and the storage bill is a fraction of keeping everything in RAM. Our explainer on running a vector database on S3 covers the economics.
Where Mixpeek fits
Mixpeek reads your files where they already live, in S3, GCS or any bucket, and turns what's in them into data your software can search, classify and moderate. Its vector store, MVS, keeps indexes in object storage with hot, warm and cold tiers, and the numbers above are its own. It fits teams searching large libraries of video, images, audio and documents who want storage priced like a bucket. It is the wrong fit if you need sub-10 ms answers on every query. Prices are on the pricing page, and the docs cover setup. If you are choosing a store, see S3-compatible object storage options and the best vector databases.
Frequently Asked Questions
Why is the first vector search query on S3 slow?
Because the index has to be read from the bucket before the query can be ranked. Each GET to S3 or GCS takes tens of milliseconds, a centroid index needs three or four sequential reads, and a layout that fetches whole partitions downloads hundreds of megabytes. In our 1M x 768 runs the cold p50 through the API was 0.697 s, against tens of milliseconds once cached.
How much slower is a cold query than a warm one?
Between about 14 and 62 times in the figures here. Our cold query at 1M x 768 had a p50 of 0.697 s. Our resident shard at 2M x 768 answered at a p50 of 50.28 ms. turbopuffer publishes 874 ms cold and 14 ms cached at 1M documents.
How many bytes does a vector search read from S3?
It depends on the layout. Reading whole partitions, our first dense query at 1M x 768 read 391 MB and a 1% filtered query read 1,580 MB. A range-read layout answered the same dense query with about 3.1 MB.
Does storing vectors in S3 hurt recall?
Recall depends on how many partitions each query searches. On a large index with thousands of partitions, a default nprobe can search too few of them. At 80.6M vectors we measured recall@10 of 0.355 at the default and 0.960 at nprobe 1,024. Measure recall against an exact search at your real size.
How long does a write take to become durable in object storage?
Tens to hundreds of milliseconds, because the write is acknowledged only after the object is in the bucket. On real GCS we measured p50 107.9 ms and p95 140.5 ms for single writes from one writer, and p95 176.6 ms with eight writers.
How do I make cold queries on S3 faster?
Fetch byte ranges instead of whole files, keep the query to three or four sequential round trips, warm or pin namespaces that need low latency, and move hot data to a faster storage class such as S3 Express One Zone. In our runs, range reads cut a cold query's bytes from 391 MB to about 3.1 MB.