PostgreSQL cache invalidation: the late-fill race
Walk through a cache-aside race where an old read refills a deleted key after commit. Understand publication fencing, snapshots, rollback and request-local caches.
Read articleRead rows by primary key over authenticated RESP MGET. PostgreSQL writes automatically invalidate affected cache entries.
Authenticated RESP API · PostgreSQL 14–18 · Open source
3.1.0 adds byte-based slab capacity for millions of small keys when the budget fits, partitioned locks, allocation-free single-key hits, cheaper write tracking, and deferred locked misses. Upgrade guide.
3.1.0 · final build c431bcc
210krequests/s · RESP MGET
Per pinned physical server core
hot keys · 256 clients · Go/pgx
An eligible hit returns the stored row. A miss or bypass reads the source table. Your ordinary SELECT queries keep their existing path.
-- Attach your table once.
SELECT local_cache.attach_table('public.items');
-- Read rows by primary key.
MGET CRUD:pglc_demo.public.items:{"id":42} CRUD:pglc_demo.public.items:{"id":7}
Authenticated and bounded to 1,024 keys per request. Missing rows return null.
INSERT, UPDATE, and DELETE invalidate affected entries.
Allocate the shared row cache at PostgreSQL startup.
Ineligible reads use the source table under PostgreSQL visibility rules.
Node.js and Go. Prepared SQL and RESP MGET.
One runner uses the same keys, batch sizes, connection counts, duration, and result checks. Compare throughput, latency, and PostgreSQL CPU and memory on your machine.
Run the comparisonChoosing a cache? Compare PostgreSQL pages, rows, materialized views, and Redis.
Clone the repository and start a disposable PostgreSQL server with sample rows. Docker builds the extension and keeps the demo separate from your databases.
git clone https://github.com/profundium/pg_local_cache.git
cd pg_local_cache
docker compose -f examples/compose.yaml -f examples/compose.resp.yaml up --build --wait
Read sample rows and check cache hits. Already cloned the repository? Run the last command from its root. For an existing server, use the installation guide.
Installing on an existing server requires a PostgreSQL restart.
Walk through a cache-aside race where an old read refills a deleted key after commit. Understand publication fencing, snapshots, rollback and request-local caches.
Read articleDesign a fair PostgreSQL row-cache comparison using prepared SQL and RESP MGET. Separate warm reads, misses, batch sizes, writes and client costs.
Read articleReplace N+1 primary-key queries while preserving duplicate IDs, input order, NULL positions and missing rows. Compare ANY, WITH ORDINALITY and RESP MGET.
Read articlepg_local_cache 3.1.0 keeps hot rows in PostgreSQL shared memory, serves them over RESP and never caches a row older than the last commit. Results against Valkey and prepared SQL.
Read articleNo. PostgreSQL caches database pages. This extension separately caches serialized whole rows by primary key. See the read-path comparison.
No. Only authenticated RESP MGET uses the shared row cache. Your existing queries keep PostgreSQL’s normal execution path.
No. The RESP2 endpoint is limited. There is no general-purpose Redis command set, TTL, pub/sub, or distributed coordination. Compare the PostgreSQL and Redis cache-aside paths.
The invalidation guide explains write behavior. The upgrade guide covers 3.0.0 to 3.1.0 and the earlier 2.x migration.