Keep hot primary-key reads inside PostgreSQL

pg_local_cache adds a narrow, transaction-aware fast path for repeated exact-primary-key and bounded single-column IN/ANY reads. PostgreSQL stays authoritative, and unsupported or unsafe queries keep the original source plan. It is not a general query cache or a universal Redis replacement.

SELECT local_cache.attach_table('public.items'::regclass);

SELECT * FROM public.items WHERE id = ANY($1::bigint[]);

SELECT local_cache.get('public.items'::regclass, $1::bigint);
SELECT local_cache.mget(
    'public.items'::regclass,
    $1::bigint[]
);
psql │ postgres@localhost:5432/app
app=> EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF)
      SELECT * FROM public.items WHERE id = 42;

Custom Scan (pg_local_cache_sql) on items (actual rows=1 loops=1)
  Cache Namespace: public.items
  Cache Policy: positive MVCC-safe entries
  On Miss: unique index scan
  Cache Hits: 1
  Cache Misses: 0
  Cache Bypasses: 0

app=> BEGIN;
BEGIN

app=*> UPDATE public.items
      SET value = 'updated' WHERE id = 42;
UPDATE 1

app=*> COMMIT;
COMMIT
-- the cache entry was fenced before commit became visible
Inside PostgreSQL

Hot whole rows live in bounded shared memory; PostgreSQL remains the source of truth.

Narrow SQL fast path

Exact PK and bounded single-column IN/ANY only; unsupported queries use the normal plan.

Operational trade-off

Requires shared_preload_libraries, one controlled restart, memory sizing, and monitoring.

Not a Redis replacement

No TTL, pub/sub, clustering, or multi-primary cache coordination.

Measured trade-offs

The latest green comparison used PostgreSQL 16.14, four clients, pipeline depth eight, 128 keys per attached table, 256 cache entries, and one timed second after counter-verified stabilization.

See exact commands, operations definitions, raw evidence, and interpretation limits →
Ordinary PK SELECT1.84–1.88x

mapped vs stock PostgreSQL

32-key SELECT IN2.64x

865,201 vs 328,282 key ops/s

Warm RESP GET0.72x Redis

143,104 vs 197,522 ops/s

The slower RESP result is shown deliberately: dedicated Redis and Valkey remain better raw protocol servers in this profile. A separate SQL MGET profile measured 10.30–10.39x its stock PostgreSQL batch, but it is a different API and workload.

Run 31172234073 · source 71b0aa3 · raw JSON · rendered report.

Install on an existing PostgreSQL server

Choose PostgreSQL 14–18 and glibc (Debian, Ubuntu, RHEL-family) or musl (Alpine). Download the stable asset directly—no GitHub CLI or tag lookup. Preparation stays online; first activation needs one controlled restart.

1Download latest

Choose the compatible binary or source archive plus checksums.

2Verify & extract

Check the published SHA-256 digest before opening the archive.

3Preflight & stage

Check the cluster, then copy files and stage settings online.

4Restart & verify

Use one controlled restart, verify health, then attach a table.

Example: PostgreSQL 18 · Linux glibc · amd64
PG_MAJOR=18
LIBC=glibc
BASE=https://github.com/profundium/pg_local_cache/releases/latest/download
curl -fLO "$BASE/pg_local_cache-pg${PG_MAJOR}-linux-${LIBC}-amd64.tar.gz"
curl -fLO "$BASE/SHA256SUMS"
sha256sum --check --ignore-missing --strict SHA256SUMS
tar -xzf "pg_local_cache-pg${PG_MAJOR}-linux-${LIBC}-amd64.tar.gz"
cd "pg_local_cache-"*-"pg${PG_MAJOR}-linux-${LIBC}-amd64"
sudo ./install.sh preflight --database app --mode sql-only
sudo ./install.sh install --database app --mode sql-only

Continue with restart, verification, source builds, HA, and rollback →

Architecture

PostgreSQL backend bounded shared row cache pre-commit invalidation fence

Documentation