Skip to content

turbopuffer in production

nate
Jul 9, 20262 min read1 read

turbopuffer in production

operational gotchas from running turbopuffer (a hosted vector database) under two systems: pub-search, a search service over atproto publications, and phi, a bluesky agent with per-user memory namespaces. none of these are in the quickstart.

ids are capped at 64 bytes

at-uris routinely exceed it. pub-search sha256-hashes the uri and keeps 32 hex chars (128 bits — collision-safe at any realistic scale). the cost: you can't recover the uri from the id, so it must also live in an attribute or an external store.

schema evolution: don't name attributes you might not have

a query listing include_attributes: ["source_uris", ...] 400s against a namespace created before that attribute existed. phi's memory queries with include_attributes: True (all attributes) and reads defensively — old namespaces return rows without the new fields instead of erroring the whole query. the alternative (migrating every namespace on schema change) is worse when namespaces are per-user and created lazily.

cold namespaces are slow

first query against a cold namespace pays ~600–900ms. pub-search runs a keepalive thread that pings every 3 minutes — dumb, effective. worth it the moment p50 matters.

attributes go stale; keep an authoritative store

attributes are written at embed time and never updated unless you patch them, so they drift from reality (a document's path changes; its tpuf attribute doesn't). pub-search stores platform/path metadata as tpuf attributes but reads the authoritative values from its local sqlite replica at query time, using the tpuf copy only when the replica misses.

missing predicates get pushed client-side

no since filtering on the ann query — date filtering happens after retrieval, client-side, with the same semantics as the sql path (lexicographic iso-8601 compare; a row with no timestamp fails the filter, because it can't prove it's in range). over-fetch accordingly.

the dummy-vector scan idiom

to page through rows regardless of similarity (export, centroid computation), query with a constant vector ([0.5] * dims) and high top_k — ann as a poor man's table scan. both phi's memory (per-user centroids for the /memory graph) and the atlas flow use this. fine for batch paths; don't put it on a hot path.

patch, don't rewrite

soft-delete via patch_rows (phi marks superseded observations in place) keeps history queryable and avoids re-embedding — the vector doesn't change when the status does.

sources

Did you enjoy this article?

Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.

Across the AtmosphereDiscussions