跳到主要内容

Tradeoffs

Every storage design gives something up. This page lists what Memlake trades away, and when Postgres is the better fit.

Writes are durable first, fast second​

Memlake keeps every write in object storage before acknowledging it, and keeps query nodes stateless. A write takes about 250 ms at p50 to commit, and p99 can reach several seconds with many concurrent writers. In return, a single bank sustained 1,200–1,500 memories per second at the storage layer in our tests, 15–27× what Postgres sustained, flat as the bank grows, and a write is searchable the moment it returns.

In practice this rarely matters: every Hindsight retain goes through an LLM first, so how fast memories land is bound by the LLM, not the store. Retains can run asynchronously (async: true), which keeps commit latency off the agent's response path.

Consistent reads have a latency floor​

Every query checks object storage for the latest writes before answering. On a cached bank this is the only object-storage request, and most of the query's time inside Memlake: about 20–30 ms at p50 on GCS in the same region. Memlake does not offer an eventually consistent mode that skips it.

Cold starts are real​

Object storage makes warm reads cheap and first reads slow. The first query to a bank on a query node opens it, reading its index metadata from object storage: 8.6 s at p95 for a 10M-memory bank, once per query node. Until its data blocks are cached, queries on large banks can take about a second at p99.

The layout is built for this case: a cold query needs a small, bounded number of sequential round trips, each fetching only the blocks it needs. Query nodes warm a bank in the background whenever they open it or pick up a new index, and banks stay routed to the same query node, so this happens rarely. If you know a bank is about to get busy, send it one recall ahead of time to start the warm-up.

Built for many banks, not one giant bank​

Memlake scales out across banks: each bank is served by one query node at a time, and load spreads across the fleet as banks are added. Agent memory usually splits naturally (per agent, user or project), so this matches how Hindsight is used. A bank's index is never split across query nodes, so a single very hot bank is limited by the query nodes it runs on. For those, the optional balancer can serve the bank from up to three query nodes, or place it on a larger, dedicated query node.

Query cost grows sublinearly with bank size​

The number of sequential storage round trips per query doesn't grow with the bank, but the CPU to scan vector clusters does, roughly with the square root of the bank: growing one bank 30×, from 100k to 3M memories, made a recall about 6× slower under heavy load. So does the number of segments each query reads, unless segment routing is on. Keeping banks scoped to one agent, user or project keeps recall fast and cheap.

Ranking policy lives in Hindsight​

Hindsight sets the ranking policy (fusion, cross-encoder reranking, boosts and the token budget) and sends it with every request. Memlake runs it next to the data and returns only the results that survive, or returns each search's raw scores for Hindsight to rank. Policy changes take effect on the next request, without touching stored data or rebuilding indexes.

Two stores, not one​

Memlake holds memories; Hindsight's operational data (banks and settings, the job queue, the audit log) stays in a small Postgres database. That keeps each system doing what it does best, but a deployment runs both, and the Postgres side still needs ordinary database care (backups, upgrades). It stays small: it holds no memories.

Single region, active/standby​

A deployment runs in one region. Disaster recovery is active/standby: the archiver copies every bank to a separate bucket, and a restore is verified before traffic moves. On a regional failure, you lose at most about one indexing interval plus one archive pass of memories; document bodies uploaded since the last daily snapshot may also be lost.

Comparison​

Hindsight supports Memlake (Hindsight Enterprise) and relational backends: Postgres by default, and Oracle 23ai. As a rule of thumb, if your banks stay under roughly 50,000 memories each, Postgres is a good fit.

Memlake excels atA relational backend may be a better fit for
Big banks (hundreds of thousands to millions of memories), and many of themSmall deployments, banks under ~50k memories, local development
Recall that stays fast as banks grow: about 0.1 s at p99, where Postgres took 0.5 s on a 4k-memory bank and 5 s at 210kThe lowest latency on small banks, with no object-storage check on each read
Cost that follows query rate: memories stored at object-storage pricesEnvironments without S3, GCS, or an object store that supports conditional writes
Fast ingest: over 1,000 memories per second per bank at the storage layer, against 55–80 on PostgresTeams that want to query memories directly with SQL
Data residency: memories stay in your own bucket, in your own accountDeployments that must keep all data in a single relational database
High availability without replicas: any query node can serve any bank

For more, see Performance and cost, Guarantees and Architecture.