Skip to content
← All issues

Readyset Weekly: Issue 80

Your Agent’s Memory Is a Hot Read Path

If you are running an AI agent in production, some of your most repeated reads may be coming from a table you have never thought of as hot. Agent frameworks keep each conversation's state in a database so the agent can pick up where it left off, writing it after every step and reading it back each time the conversation resumes, whether that's the user's next message or a human approving a paused step.

LangChain's LangGraph is the common example: its checkpointers persist a thread's state to a backend, and the recommended production backend is Postgres, since the in-memory option loses everything on restart (LangGraph persistence docs). On Postgres, that state lands in tables the checkpointer creates for you, mainly checkpoints, checkpoint_blobs, and checkpoint_writes. The same pattern shows up beyond LangGraph, and on MySQL too: Google's ADK, which runs on Postgres or MySQL, keeps conversation history in sessions and events, with shared state in app_states and user_states (ADK session docs).

Here is why that turns into load you might not expect.

  • An agent works in steps. It calls a model, uses a tool, reads the result, calls the model again, and a single user request can run through twenty or thirty of these before it finishes. LangGraph saves a checkpoint after every one of those steps, by default writing the full value of each state field, and reads the latest checkpoint back each time the thread resumes, whether that's the user's next message, an approval on a paused step, or a new worker picking the thread up after a restart. Every turn of every conversation runs the same lookup against the same tables. The tables are not as small as they look. Without a retention policy, a new checkpoint lands on every step of every thread and nothing expires them. The self-hosted Postgres checkpointer has no built-in TTL, so checkpoint_blobs, where the message history lives, keeps growing until someone acts on it.

  • Each read also grows with the conversation. State for an agent usually includes the running message history, so the history in checkpoint_blobs starts small but can carry a long transcript by the end of a session. Each resume pulls back more than the last, and by default every step writes the full history back again, so a handful of active threads reading their own growing history can put steady pressure on the database that has nothing to do with your application's own traffic.

  • It is easy to miss because the symptoms point elsewhere. The agent is slow to start a turn, and the delay gets blamed on the model, and the database looks fine on the metrics people usually watch, since no single query is slow. What adds up is the same lookup on every turn, returning more each time. You can see it in pg_stat_statements on Postgres or performance_schema on MySQL by checking the checkpoint queries' call counts and rows returned. A repeated lookup on the same key is the shape a cache is built for.

If your agent's state lives in Postgres or MySQL, Readyset can take those repeated reads off your database. It sits in front of your database and serves the turn-start lookups from cache, keeping the results current as the state changes, so the agent gets its context back faster without moving memory out of your primary database or rewriting how the agent stores it. And because agents read back what they just wrote, Readyset can route a session's reads to the primary for a set window after each write, so a fast resume always sees the latest state.

🔍 What We're Watching in the Database Ecosystem

Postgres recovery_prefetch considered harmful and causing outages

A standby that loses its walreceiver replays from pg_wal instead, and recovery_prefetch turns the primary's sequential I/O into 8 kB random reads. On a 500 IOPS disk, the standby sat at its limit for about 40 minutes, and because replication was synchronous, every write on the primary hung for over half an hour. Jeremy Schneider published a repro, and with recovery_prefetch=off the same update applied in under 10 minutes.

MySQL 26.10 Innovation released

MySQL 26.10 ships alongside 9.7.4 and 8.4.13 LTS. Closing a read view under READ COMMITTED no longer takes a global mutex and copies the active transaction set, which matters most on NUMA hardware. Crash recovery no longer writes out every page it loaded after the final redo batch, only the ones with changes, and a stale adaptive hash index entry could previously return rows from a discarded tablespace after a tablespace import.

A JSON column round-trip changed 11% of float values on MySQL 8.4

A hash-chained audit log that verified correctly on SQLite broke on MySQL at event 34. MySQL parses JSON into a binary format and re-renders it on the way out, and 1,075 of 10,000 random doubles came back with a different last digit on 8.4.10. MariaDB, which stores JSON as checked LONGTEXT, changed none.

In Case You Missed It

New on our blog:

Less Work per Query: Speeding Up Readyset's Text Protocol

Prepared statements are faster because they parse and plan once, but plenty of teams run ad hoc SQL and can't change the application to fix it. Our latest release optimizes Readyset's text-protocol path with about a dozen changes, like skipping no-op rewrites and looking up only the schema a query touches. By doing less work per query, they deliver 2.7x more throughput on a warmed cached point-read test at 1,024 clients.

Black October: What Actually Happened, and What to Patch

October's wave of database security news looked like AI swarms breaking the internet, but after reviewing all 28 incidents against primary sources, the real damage came from long-lived credentials sitting in shared places like CI/CD and hosted SQL consoles, while AI mostly helped defenders find bugs. The post sorts out what to actually patch, from PgBouncer 1.26.1 to the LiteLLM compromise, and what to rotate.

A TTL Cache Needs to Know the Query

A TTL cache on Redis or Memcached holds bytes, so it can't re-run a query to refresh a hot key, collapse a burst of misses into one call, or tell a 900 ms result from a 2 ms one. The post covers where that breaks under load, and what a query-aware cache does instead.

We're getting more social, catch the latest on Bluesky, X, dev.to, and Medium too.

Know a database aficionado who'd appreciate this? Forward it their way.

Get Readyset Weekly in your inbox

One email a week — what shipped, what we learned, and what is worth reading.