Changelog

0.7.0 — 2026-09-25

Consistency

  • Read-your-own-writes — each connection now tracks the writes it forwards to origin. Until CDC has applied a write, a cacheable read on the same connection is served from cache only if pgcache can prove the read is disjoint from it; otherwise it is forwarded to origin.
    • Writes through an auto-updatable view, or directly into a partition child while the parent is cached, are not matched to reads of the underlying table.
  • Cache hits inside transactions — cacheable reads inside a READ COMMITTED transaction block are now served from cache when they are disjoint from the block’s own uncommitted writes. Previously every statement inside a block was forwarded. Blocks at REPEATABLE READ or SERIALIZABLE, and failed blocks, are still forwarded.

Performance

  • Elastic population worker pool — population work goes through one shared queue served by whichever worker is idle, replacing per-worker round-robin queues where one slow item could strand its lane. The pool grows and shrinks dynamically. Bounds are the new population_workers_min / population_workers_max settings.
  • Elastic serve pool — the cache-database serve pool, previously fixed at num_workers × 2 (min 4), now sizes itself between num_workers × 2 and num_workers × 8. It grows under cache-hit load that queues on the pool and releases idle connections when load falls.
  • Narrower TOAST fallback during population — an unrepairable unchanged-TOAST update on a relation with an in-flight population now aborts only the populations that staged the affected row, instead of invalidating every cached query over the relation.

Bug Fixes

  • Text range subsumption under non-C collations — subsumption compared string range bounds by byte order, which differs from collation order ('a' > 'B' in bytes, 'a' < 'B' under en_US). A cached query such as WHERE name > 'B' could be used to serve WHERE name > 'a' and miss rows. Range bounds on quoted literals (text, and dates or timestamps written as strings) are no longer used for subsumption; equality and IN-set coverage are unaffected.
  • Dead population cache connection — a population worker whose cache-side connection died (backend killed, cache reset) now reconnects it, as it already did for its origin connection. Previously that worker failed every population it received from then on.
  • Client hang during cache restart — a cache serve waiting for a pool connection while every serve connection was lost at restart now closes the client connection instead of waiting forever.

Configuration

  • New population_workers_min setting (default max(num_workers, 2)) — floor and starting size of the population worker pool. Each worker holds one origin and one cache connection.
  • New population_workers_max setting (default num_workers × 8) — ceiling on concurrent population queries against origin. An explicit population_workers_max below the default floor lowers the floor with it.

Observability

  • Read-after-write metrics — pgcache.raw.writes_recorded, pgcache.raw.probes (commit-LSN probes sent to origin), pgcache.raw.forwards (labeled scope=table|connection), pgcache.raw.forward_blocked (labeled cause=unstamped|delivery_lag|apply_lag), pgcache.raw.clearance_seconds (histogram: time from a write’s commit probe to its clearance), pgcache.raw.serve_disjoint_insert / _update / _delete (reads served because they were proven disjoint from a pending write), pgcache.raw.cap_degraded_insert / _update_delete, and pgcache.raw.tier_merges.
  • In-transaction serving metrics — pgcache.txn.served, pgcache.txn.forwards (labeled reason=failed|isolation_unknown|isolation_strict|pending_write), and pgcache.txn.isolation_probes.
  • Elastic pool metrics — pgcache.cache.population_workers and pgcache.cache.serve_pool_size (gauges: live pool size), pgcache.cache.population_scale_up / _scale_down / _backstop_grows and pgcache.cache.serve_pool_scale_up / _scale_down / _backstop_grows (counters), and pgcache.cache.population_queue (gauge: shared population queue depth).
  • pgcache.cache.cdc_toast_stale_aborts — population merges aborted and re-run because their staging held a row an unrepairable TOAST update left stale.
  • cdc.settled_lsn in /status — every origin transaction committing at or below this LSN is either applied to the cache or produced no change pgcache consumes. Unlike last_applied_lsn, it also advances across WAL with nothing to apply (DDL, unpublished tables, idle origin).

Breaking Changes

  • /status cdc.apply_idle removed — replaced by cdc.settled_lsn. To wait until pgcache has applied everything up to an origin LSN, poll until settled_lsn reaches it.
  • pgcache.cache.population_worker_queue removed — the per-worker gauge (labeled worker) is replaced by the single pgcache.cache.population_queue gauge.

0.6.4 — 2026-09-18

Bug Fixes

  • Memory growth with many connections — every connection kept its own parsed copy of the cacheability analysis for each distinct SQL text it had seen. Analyses are now interned once per process and shared across connections.
  • Unbounded histogram samples when /metrics is never scraped — latency histogram samples were only drained on a /metrics render, so a process that was never scraped retained every sample it had recorded. Samples are now drained once a second regardless of scraping.
  • CDC apply no longer waits behind a population merge — a population’s staging→cache merge ran as one statement per relation on the writer thread, so no CDC change applied until it finished. The merge now drains staging in bounded chunks with CDC applied between them.
  • Abandoned population staging is emptied in chunks — when a population is superseded, invalidated, evicted, aborted, or fails, its staging tables used to be emptied with one DELETE on the writer, stalling CDC apply for seconds on large populations. They are now drained in the same bounded chunks as a merge, with CDC applied between them.
  • Eviction no longer strands a query mid-population — under a query-count cap, a pinned or CLOCK-referenced query whose population was still in flight could have its generation bumped by the eviction tick, abandoning the population with nothing to finish or readmit it, so it stayed Loading forever. Such candidates are now skipped for the round and bumped once Ready.
  • Dual-stack admin listener — the admin HTTP server bound to 0.0.0.0 is now reachable over IPv6 loopback too, so a container healthcheck against localhost:9090 works on images whose /etc/hosts resolves localhost to ::1 first. The bound admin address is logged at startup.
  • Docker image healthcheck — the image now declares a HEALTHCHECK on /healthz; the documented compose examples used curl, which the image does not ship.

Observability

  • pgcache.connections.cacheability_entries (gauge) — interned cacheability analyses currently referenced by live connections; bounded by distinct SQL texts in use, not by connection count.
  • pgcache.cache.merge.chunks_total (counter) and pgcache.cache.merge.chunk_seconds (histogram) — merge chunks applied and their wall time, the longest a CDC frame waits behind an in-progress merge.
  • pgcache.cdc.keepalive_marks_coalesced (counter) — a run of consecutive replication keepalives queued for the writer is now handled as one at its highest LSN, so a keepalive burst from a busy origin cluster costs the writer one iteration instead of one each.

0.6.3 — 2026-09-11

Performance

  • Materialized-view rebuild backoff — a query whose MV keeps getting dirtied right after a rebuild now backs off exponentially (1 s → 300 s) after two consecutive wasted builds instead of rebuilding on every hit.
  • Broader subsumption matching — a new query mixing equality and range predicates (tenant_id = 7 AND created_at > 150) can now be served from a cached equality-only parent (tenant_id = 7).

Reliability

  • Cgroup memory budget — the memory monitor now honors the tightest limit on the cgroup ancestor chain (e.g. systemd MemoryMax=) and measures usage as the working set (excluding reclaimable page cache), so the cache database’s page cache no longer trips the registration throttle.
  • Serve-pool replenishment on task death — a serve task that panics or is cancelled while holding a connection now triggers a replenish. Protocol desyncs poison the connection at detection instead of waiting out the stall deadline.
  • Non-blocking log writes — log lines go through a queue and a dedicated writer thread; a stalled stdout reader can no longer block a worker thread. On overflow lines are dropped and counted.

Bug Fixes

  • Self-joins — CDC membership is evaluated against every occurrence of a self-joined relation; a row needed only by one arm could previously be evicted from the cached result.
  • Enum and other origin-only column types — tables with such columns no longer have their cache table recreated on every CDC relation message. Order-dependent enum usage (ORDER BY, range comparisons, min/max) is now forwarded, since enums are stored as text in the cache.
  • Identifiers that require quoting — mixed-case names, reserved keywords, and embedded quotes now survive cache DDL, population, CDC apply, and deparse.
  • Quantified ANY comparisons — x > ANY (SELECT …) and other non-equality ANY sublinks are now forwarded instead of being cached with IN semantics.
  • * over derived tables — USING and NATURAL joins over subqueries no longer drop columns from the cached result.

Configuration

  • New --config_create flag — a missing config file is no longer a startup error; it is created by the first PUT /config, so dynamic configuration changes persist.

Observability

  • CDC watermarks in /status — cdc.last_applied_lsn now advances only on applied cache commits; the receive/liveness watermark is the new cdc.last_received_lsn. A new cdc.apply_idle flag reports whether the apply pipeline has nothing in flight.
  • MV backoff — pgcache.cache.mv_builds_suppressed (counter) and per-query mv_wasted_builds / mv_backoff_remaining_ms in /status.
  • pgcache.cache.serve_desync_total — unexpected backend frames caught in the serve relay.
  • pgcache.log.dropped_lines — log lines dropped because the stdout consumer is not keeping up.
  • Cacheability errors name the offending construct — the uncacheable SELECT debug log now says e.g. Unsupported FROM clause: FULL JOIN rather than a generic category.

Breaking Changes

  • /status cdc.last_applied_lsn semantics changed — consumers that compared it against the origin LSN for liveness should switch to cdc.last_received_lsn.

Internals

  • The proxy runtime is behind a default proxy Cargo feature; cargo build is unchanged, --no-default-features yields the query-analysis library only.

0.6.2 — 2026-07-03

Reliability

  • Slow populations no longer hold coalesced waiters hostage — when concurrent requests coalesce onto a single in-flight population, a waiter whose population runs past a forward deadline is now degraded to origin while the population completes in the background. This decouples serve latency from population latency, so a large cold first-population or a re-population running past its estimate no longer stalls the requests waiting on it.

Performance

  • More precise CDC invalidation under REPLICA IDENTITY FULL — UPDATE and DELETE events are now probed against the recovered old-row values rather than a primary-key-only wildcard, so changes to joined tables invalidate fewer cached queries.
  • Candidate-narrowed CDC invalidation and memo eviction — the invalidation and in-process memo-eviction passes now run against the per-operation candidate sets produced by the shared constraint-containment probe, replacing full-set scans.
  • Lower CDC apply overhead — the per-event CDC hot path was reworked to avoid unnecessary work and allocations.
  • Lower population and escaping overhead — population row tuples and string literals are escaped directly into the output buffer without per-column or temporary strings.
  • More precise LIMIT-window maintenance — changes to a column used in a cached query’s sort key is now analyzed to see if the query can be maintained in place instead of invalidating.

Observability

  • Population merge-stage metrics — pgcache.cache.merge.pending_depth (gauge: staged populations awaiting the merge drain gate), pgcache.cache.merge.wait_seconds (histogram: time from merge-enqueue to merge-apply), and pgcache.cache.merge.applied_total (counter: merges applied). Together with the existing population.wait/population.task histograms these localize a population stall to the queue, fetch, or merge stage.
  • pgcache.cache.coalesce_deadline_forward_total — counts coalesced waiters forwarded to origin because their population exceeded the forward deadline.
  • Accurate CDC invalidation counts — the CDC invalidation counter now increments once per Ready → Invalidated transition, so the metric no longer over-counts.

Bug Fixes

  • MV rebuild uses DELETE instead of TRUNCATE — rebuilding a materialized-view table now clears it with DELETE, avoiding the TRUNCATE-related locking/visibility issue.

0.6.1 — 2026-06-22

Query Language Support

  • Window frame clauses — explicit ROWS / RANGE / GROUPS BETWEEN ... frames are now preserved through parsing, resolution, and deparse, so windowed queries with a custom frame are cached.
  • Named windows — WINDOW w AS (...) definitions and their OVER w references are now resolved and cached. Previously OVER w could not be resolved and the query was forwarded.

Performance

  • Shape-keyed prepared statements on cache hits — cache serves now reuse one prepared statement per query shape (literals bound as parameters) instead of re-parsing per distinct literal.
  • Replicated partial and expression indexes — partial (WHERE-qualified) and expression indexes on origin tables are now recreated on the corresponding cache tables.
  • Indexed CDC evaluation — a shared per-relation constraint-containment index narrows which cached queries a CDC change must be checked against, replacing per-relation full scans.
  • Lower-overhead population — population now reuses pooled per-relation staging tables instead of issuing per-population DDL, and each population worker uses its own dedicated origin connection.

Reliability

  • Adaptive registration pacing — new-query registration is paced to the cache writer’s measured drain rate. Registration bursts are shed to origin instead of saturating the writer and degrading already-cached query latency.
  • TOAST-safe CDC updates — UPDATE events that omit unchanged TOASTed columns (Postgres’s unchanged-toast marker) are now repaired from the cached row via a single batched lookup per relation, rather than being mis-applied. Cases that cannot be repaired conservatively invalidate the affected query.
  • Protocol desync fix on serve failure — a cache-serve error that occurs after bytes have already been sent to the client now terminates the response with a synthetic ErrorResponse instead of emitting a duplicate BindComplete.
  • Correct alias quoting — resolved-SELECT deparse now quotes column aliases that require it, fixing result column names for aliases containing special characters or matching keywords.
  • Faster shutdown during an origin outage — CDC reconnect-probe awaits are cancel-guarded so teardown is no longer blocked behind an origin connect timeout.

Configuration

  • Disk-pressure eviction; cache_size deprecated — pgcache now measures real cache-volume usage (via statvfs) and evicts under disk pressure. cache_size is now deprecated and ignored (setting it logs a warning). The optional new disk_limit caps cache-volume bytes; when unset it auto-derives from the volume’s live free space, keeping a reserve free (5% of capacity, clamped 1–10 GiB); a configured value will only lower the auto-derived ceiling.
  • New mv_compute_min_rows setting (default 1000) — adds a compute-avoidance path to the materialized-view first-build gate: a shape-eligible query is now materialized if either its result is sufficiently smaller than its input (the existing mv_size_ratio row-reduction test) or its population source-row count reaches mv_compute_min_rows, so large inputs aren’t recomputed from source rows on every serve. Settable via TOML (mv_compute_min_rows), CLI (--mv_compute_min_rows), env (PGCACHE_MV_COMPUTE_MIN_ROWS), and at runtime via PUT /config.
  • Native environment variables — mv_size_ratio, mv_compute_min_rows, memo_cache_size, memory_limit, and disk_limit can now be set directly via PGCACHE_* environment variables (in addition to TOML and CLI).

Observability

  • Cache-volume disk metrics — pgcache.cache.disk_total_bytes, pgcache.cache.disk_available_bytes, pgcache.cache.disk_used_bytes, and pgcache.cache.disk_limit_bytes (the effective disk-usage ceiling above which reclaim engages).
  • Materialized-view build metrics — pgcache.cache.mv_build_queue (gauge: build tasks waiting for a build-pool connection) and pgcache.cache.mv_gate (counter, labeled outcome=admit|reject: first-build gate decisions).
  • CDC TOAST-repair metrics — pgcache.cache.cdc_toast_repairs and pgcache.cache.cdc_toast_fallbacks (unchanged-TOAST images repaired from cache vs. routed to conservative invalidation).
  • Serve-pool metrics — pgcache.cache.serves_in_flight, pgcache.cache.pool_available, pgcache.cache.serve_stall_total (serves that exceeded the stall deadline and were forwarded to origin), and pgcache.cache.serve_dirty_return_total (serves that finished with unconsumed bytes — a response-desync guard).
  • Registration-gate metrics — pgcache.cache.reg_gate_rate, pgcache.cache.reg_gate_btlbw, pgcache.cache.reg_gate_queue_min, pgcache.cache.reg_gate_drain_rate, and pgcache.cache.reg_gate_loading expose the adaptive registration pacer’s internal state.
  • /status additions — a root fault_injection flag (whether the binary was built with the fault-injection feature) and a per-query mv_state field (materialized-view state-machine position).

Breaking Changes

  • Removed the Prometheus gauges pgcache.cache.size_bytes and pgcache.cache.size_limit_bytes, superseded by the pgcache.cache.disk_* gauges. The /status JSON size_bytes / size_limit_bytes fields remain, now reporting cache-volume used bytes and the effective disk limit respectively.

0.6.0 — 2026-06-10

Reliability

  • Population/CDC merge consistency — query population and the live CDC stream are two independent writers to the same cache tables, observing origin at different points on the replication timeline. pgcache now coordinates them so the data in the cache is always consistent.
  • Bounded memory under high query cardinality — pgcache no longer grows its in-process state without limit. A memory monitor throttles registration of new distinct queries as whole-system used memory approaches a budget (80% of detected RAM by default, cgroup-aware in containers). Already-cached queries keep serving.
  • Serve-pool connection recycling under memory pressure — cache-database serve-pool connections are now periodically recycled to reclaim the per-connection plan-cache memory Postgres accumulates, keeping the cache backend’s footprint bounded over long runs.

Performance

  • Prepared, pipelined CDC evaluation — per-query CDC membership and row-change checks are now prepared once and pipelined rather than re-parsed per event. Membership is evaluated in batch, lowering CDC apply cost.

Configuration

  • New optional memory_limit setting — an absolute ceiling (bytes) on pgcache’s total memory footprint. Unset → 80% of detected RAM; a configured value can only lower the effective ceiling. Settable via TOML (memory_limit), CLI (--memory_limit), env (PGCACHE_MEMORY_LIMIT), Docker (MEMORY_LIMIT), and at runtime via PUT /config.
  • Automatic disk-size limit; cache_size deprecated — when cache_size is unset, pgcache now derives the cache’s disk-size limit from the cache volume’s live free space, evicting to keep a reserve free (5% of total capacity, clamped between 1 GiB and 10 GiB). A configured cache_size remains a hard override but is deprecated, a new disk_limit setting will be added analogous to the memory_limit setting.

Observability

  • Memory and registration-throttle metrics — pgcache.cache.memory_used_bytes, pgcache.cache.rss_bytes, pgcache.cache.memory_budget_bytes, pgcache.cache.query_count_cap, pgcache.cache.marginal_bytes_per_query, pgcache.cache.registration_throttled, and pgcache.cache.registration_throttled_total.
  • CDC prepared-eval metrics — pgcache.cache.cdc_prepared_hits and pgcache.cache.cdc_prepared_misses track the prepared-statement cache for CDC evaluation (a high miss rate signals the live query working set exceeds the cache capacity).
  • pgcache.cache.pool_recycled — counts serve-pool connections recycled to reclaim plan-cache memory.

0.5.0 — 2026-06-07

Performance

  • Unified serving runtime — connections, the request coordinator, and the cache worker now run as tasks on a single shared multi-threaded runtime instead of separate per-thread executors.
  • In-process result memo — a new in-memory tier caches full result snapshots for the hottest queries and serves them inline skipping the cache-database round-trip entirely. Controlled by the new memo_cache_size setting (default 64 MiB; 0 disables); adjustable at runtime via the admin API.

Reliability

  • Cache restart supervisor — backend failures (writer, CDC, or connection-pool death) self-heal. A supervisor rebuilds the cache subsystem with exponential backoff (500 ms → 30 s) while the proxy keeps serving by forwarding to origin.
  • Serve-pool connection replenishment — a poisoned cache-database connection is now discarded and transparently replaced rather than degrading the serve pool.

Configuration

  • New memo_cache_size setting (default 64 MiB) — total byte budget for the in-process result memo; 0 disables it. Settable via TOML (memo_cache_size), CLI (--memo_cache_size), the PGCACHE_MEMO_CACHE_SIZE environment variable, and at runtime via PUT /config.

Observability

  • In-process result memo metrics — pgcache.cache.memo_hits, pgcache.cache.memo_captures, pgcache.cache.memo_evictions, pgcache.cache.memo_entries, and pgcache.cache.memo_bytes.
  • Restart and pool metrics — pgcache.cache.restarts_total (successful cache-subsystem restarts) and pgcache.cache.pool_replenished (replaced serve-pool connections).
  • pgcache.protocol.close_local — counts Close(statement) messages handled locally without forwarding to origin.

Breaking Changes

  • Removed the internal queue-depth metrics pgcache.cache.proxy_message_queue and pgcache.proxy.worker_queue — the per-connection and coordinator message queues they measured no longer exist under the new runtime.

Internals

  • num_workers now sizes the shared runtime’s worker-thread pool; it remains the multiplier for the connection-pool, command-channel, and population-pool sizes.

0.4.12 — 2026-06-01

Query Language Support

  • USING, NATURAL, and CROSS JOIN — these join forms are now parsed, resolved, and cached. USING and NATURAL resolve to the equivalent equi-join; CROSS JOIN (and a NATURAL join with no common columns) is treated as a cartesian product. FULL JOIN is still forwarded.
  • Typecasts on WHERE-clause values — WHERE col = '2026-01-01'::date and similar casts on literals are now parsed and usable both on the CDC fast path and for subsumption. (0.4.11 added typecasts in the select list.)
  • Parameters in the SELECT list — extended-protocol parameters in the target list (e.g. SELECT $1, col FROM t) are now handled.
  • NULLS FIRST / NULLS LAST — explicit null ordering in ORDER BY is now supported.

Performance

  • Subsumption index covers non-equality predicates — the subsumption index now indexes inequalities and two-sided ranges, not just equality literals, so more cached queries serve narrower requests via the indexed path instead of a linear scan.
  • Direct pg_query parse-tree reads — query analysis reads libpg_query’s parse tree directly instead of round-tripping through protobuf.
  • Combined EXISTS evaluation — per-query EXISTS predicates are evaluated in a single pg_eval query rather than one query per predicate.
  • Lower CDC and forward-path overhead — CDC frame writes are buffered and flushed in a single round-trip; the query_row_changes SELECT is skipped for relations a change cannot affect; metric handles are resolved once into a grouped struct, eliminating per-emit key hashing.
  • Extended-protocol Parse/Describe caching — synthesized Parse+Describe messages are cached and Parse is sent to the origin lazily, only when needed on the forward path. Handling of multiple Parse/Bind/Describe/Execute sequences before a Sync was improved.
  • LIMIT-aware materialization — materialized-view analysis now handles LIMIT queries.

Reliability

  • Transaction-aligned replication apply — pgcache now respects the BEGIN/COMMIT markers in the logical replication stream, applying each source transaction atomically.
  • Deadlock recovery — CDC stream processing recovers from deadlock errors and the population worker retries on deadlock. Population batches are sorted by primary key to prevent ON CONFLICT deadlocks.
  • In-order client responses — an explicit ordering queue enforces the order of responses sent back to the client.
  • Clean thread shutdown — the CDC and worker threads now exit when the writer thread exits.

Bug Fixes

  • LIMIT invalidation on window-column updates — updating a column that defines a window now invalidates LIMIT-cached rows.
  • Scalar context for aggregate FILTER subqueries — subqueries inside aggregate FILTER clauses are correctly treated as scalar context.

Observability

  • More precise mv_fallthrough — pgcache.cache.mv_fallthrough now reflects the actual materialized-view fast-path-vs-fallthrough decision, and join queries are routed through the measured MV get path.
  • New protocol metrics — pgcache.protocol.describe_cache.{hits,misses,evictions,invalidations} and pgcache.protocol.lazy_parse_forwarded surface the extended-protocol Parse/Describe cache and lazy-parse behavior.

Breaking Changes

  • Removed the pgcache.cache.freshness_hits counter.

0.4.11 — 2026-05-14

Query Language Support

  • Arithmetic constant folding — WHERE x = 1 + 2 now fingerprints identically to WHERE x = 3
  • Modulo operator — % is now supported in expressions.
  • ::<type> typecast in select lists — SELECT col::int FROM t and similar typecast expressions in the target list are now parsed and cached.

Performance

  • Subsumption index — replaces the previous O(N) per-relation scan with an indexed lookup keyed on column-set + equality literals.
  • Forward-path latency — reduced the work done on the cache-miss / forward path.
  • Incremental active_relations — the publication-managed relation set is now maintained via refcounts on register/evict instead of being rebuilt by scanning the cached-query table.
  • Stale-entries cleanup off the Ready hot path — periodic Pending/Invalidated cleanup no longer runs inline with cache-Ready notifications.

Observability

  • Per-stage forward and coalesce timings — new histograms pgcache.query.stage.forward_decision_seconds, pgcache.query.stage.coalesce_intake_seconds, and pgcache.query.stage.coalesce_wait_seconds surface latency on the cache-miss and request-coalesce paths that prior lookup_seconds did not capture.
  • Writer command instrumentation — pgcache.cache.writer.command_handle_seconds reports per-command handler latency, labeled by command variant.
  • Query registration phase timings — pgcache.cache.writer.register.{resolve,subsumption_check,subsume,insert,publication_update,populate_dispatch}_seconds and pgcache.cache.writer.resolve.{update_queries_register,deparse}_seconds break down query_register into its constituent phases.
  • Population pipeline timings — pgcache.cache.population.{task,stream,wait,worker_idle}_seconds measure per-task duration, streaming time, channel wait, and per-worker idle time.
  • Scaling signals — pgcache.cache.writer.update_queries_total and pgcache.cache.writer.update_queries_max_per_relation correlate per-Register cost against state size.

0.4.10 — 2026-05-08

Query Language Support

  • FILTER clause on aggregates — agg(...) FILTER (WHERE ...) is now parsed, resolved, and cached, including when used inside window function calls (agg(...) FILTER (WHERE ...) OVER (...)).
  • Additional binary parameter types — extended query protocol parameters in binary format are now decoded for BYTEA, NUMERIC, DATE, TIME, TIMETZ, TIMESTAMP, TIMESTAMPTZ, INTERVAL, INET, CIDR, MACADDR, and MACADDR8.

Performance

  • Subsumption across binary array parameters — WHERE col = ANY($1) queries with binary array parameters now extract IN-set constraints and subsume each other (a cached ... = ANY(ARRAY[1,2,3]) serves a request for ... = ANY(ARRAY[1,2])).

Bug Fixes

  • WHERE-clause provenance in constraints — the constraint analyzer now distinguishes a query with no WHERE clause from one whose WHERE clause has terms the analyzer doesn’t support.
  • CDC invalidation walks FILTER/ORDER BY/OVER — subquery node collection for invalidation now traverses aggregate-filter, aggregate-order, and window-OVER expressions. Previously, subqueries inside these clauses could miss invalidation.

Observability

  • Applied LSN tracking — /status now reports cdc.last_applied_lsn, the highest LSN whose effects (cache mutations and invalidations) have been fully applied by the writer thread. It advances on transaction-commit and keep-alive markers and is transaction-aligned.
  • New Prometheus gauges — pgcache.cdc.received_lsn, pgcache.cdc.flushed_lsn, and pgcache.cdc.applied_lsn.
  • Improved diagnostic logs — CDC connection failures and database errors now log full error chains.

Breaking Changes

  • /status cdc object no longer includes last_received_lsn, last_flushed_lsn, or lag_bytes. These remain available as Prometheus gauges (pgcache.cdc.received_lsn, pgcache.cdc.flushed_lsn, pgcache.cdc.lag_bytes).

Internals

  • Refactored CDC LSN handling so applied LSN flows from the writer thread as its source of truth, decoupled from CDC-processor-local received/flushed LSNs.
  • numeric_to_text writes into a single pre-sized buffer.
  • LiteralValue cast fields use EcoString.
  • query/transform/parameters.rs split into focused submodules.
  • Integration tests now use LSN- and status-based settling instead of fixed sleeps.

0.4.9 — 2026-04-24

Features

  • Materialized query results — for queries that reduce a large input to a small output (aggregates, window functions, GROUP BY, DISTINCT), pgcache now maintains a second tier of cache that stores the result rather than recomputing it from source rows. When stale, the serve path falls back to evaluating over source rows.
  • Request coalescing — multiple concurrent requests for a query that’s currently loading now share a single backend execution. Subsequent requests wait for the in-flight load instead of triggering parallel populations.

Compatibility

  • Pre-PG18 search_path change tracking — PostgreSQL 16 and 17 now support session-level search_path changes. pgcache piggybacks SHOW search_path onto detected mutations (SET/RESET search_path, DISCARD ALL, transaction boundaries) to track the current value. PG 18+ continues to use the native ParameterStatus notifications.

Configuration

  • Default admission_threshold changed from 2 to 1 — queries are now cached on first occurrence by default.
  • New mv_size_ratio setting (default 10) — controls the materialization size gate for shape-eligible queries. Adjustable at runtime via the HTTP admin API.

Internals

  • Local CDC update evaluation — simple update queries are now evaluated directly in Rust against cached rows for in-place updates, avoiding a round-trip to the cache database.

0.4.8 — 2026-04-10

Features

  • Range-based predicate subsumption — subsumption now handles inequality and BETWEEN constraints. A cached WHERE value > 50 serves narrower queries like WHERE value > 100, WHERE value BETWEEN 60 AND 80, or WHERE value = 100.
  • IN-set constraint support — WHERE col IN (v1, v2, ...) is extracted as a first-class constraint used for subsumption (subset, point, and range checks) and CDC row filtering. NOT IN is handled as individual inequality constraints.
  • Dynamic runtime configuration — cache_size, cache_policy, admission_threshold, allowed_tables, and log_level can be adjusted at runtime via new HTTP admin endpoints (GET /config, PUT /config, POST /config/reload). The TOML file remains the source of truth and is updated in place with comments preserved.
  • Anonymous telemetry — PgCache reports coarse usage statistics (version, OS, Postgres version, cache hit rate, approximate query volume) to help guide development. Telemetry is enabled by default and can be disabled with PGCACHE_TELEMETRY=off, telemetry = false in TOML, or --telemetry_off on the CLI. See Telemetry for details.

Internals

  • Constraint propagation through join equivalences now handles IN-sets (e.g., a.id IN (1,2) AND a.id = b.id propagates to b.id IN (1,2))

0.4.7 — 2026-03-27

Reliability

  • CDC connection resilience — when the replication connection is lost, pgcache now preserves the cache and transparently forwards all queries to the origin database. On reconnect, it verifies the replication slot’s LSN hasn’t advanced past its last acknowledged position; if safe, cache dispatch resumes without a full restart. If the slot was dropped or the LSN diverged, pgcache performs a clean restart. Reconnection uses exponential backoff (500ms–30s).

Internals

  • Improved LSN tracking in keep-alive handling — the CDC processor now advances its position to the logical decoder’s read cursor on keep-alive messages, enabling more accurate reconnect decisions
  • Set TCP_NODELAY on sockets

0.4.6 — 2026-03-23

Features

  • Per-query operational metrics — the /status endpoint now reports detailed metrics for each cached query: hit_count, miss_count, invalidation_count, eviction_count, readmission_count, subsumption_count, population_count, total_bytes_served, population_row_count, and cache_hit_latency histogram (p50/p95/p99/min/max/mean)
  • Duration tracking — per-query idle_duration_ms (time since last hit), registered_duration_ms (time since first seen), cached_duration_ms (time since last population), and last_population_duration_ms
  • Global cache status metrics — /status cache object now includes queries_registered, uptime_ms, cache_hits, and cache_misses

Internals

  • Updated for latest hotpath profiling API changes
  • Updated and cleaned up dependencies

0.4.5 — 2026-03-16

Features

  • Admin HTTP endpoints — the metrics HTTP server now also serves /healthz (liveness), /readyz (readiness), and /status (JSON cache, CDC, and query status).
  • Database mismatch detection — when a client connects requesting a different database than the cache is configured for, pgcache logs a warning and proxyies all queries to origin for that connection.

Performance

  • Pool sizing from num_workers — cache connection pool and population worker pool sizes now scale automatically based on num_workers

Bug Fixes

  • SELECT * column order — fixed SELECT * returning columns in non-deterministic order from cache.

Observability

  • New per-stage timing metrics: pgcache.query.stage.queue_wait_seconds, pgcache.query.stage.conn_wait_seconds, pgcache.query.stage.spawn_wait_seconds — measure time waiting in worker queue, waiting for a cache database connection, and waiting for worker task spawn

Internals

  • Replaced hand-rolled HTTP server with hyper
  • Refactored TableMetadata column storage from BiHashMap to ColumnStore (ordered Vec + name index)
  • Refactored test utilities into separate submodules (context, assertions, http, metrics, process, pgproto)
  • Updated and cleaned up dependencies

0.4.4 — 2026-03-07

Reliability

  • Graceful shutdown — SIGINT now triggers coordinated shutdown via cancellation tokens propagated to all threads. The publication and replication slot are removed during the shutdown.

Breaking Changes

  • CLI argument --cache_tables renamed to --allowed_tables to match the TOML configuration field name

0.4.3 — 2026-03-06

Features

  • Table allowlist — restrict caching to specific tables via allowed_tables configuration. Queries referencing non-allowlisted tables are forwarded to the origin.
  • Pinned queries — pre-populate and permanently cache specific queries at startup via pinned_queries configuration. Pinned queries are protected from eviction and automatically re-populated after CDC invalidation
  • Pinned tables — convenience configuration (pinned_tables) that expands table names into SELECT * FROM {table} pinned queries
  • Predicate subsumption — skip cache population when a new query’s data is already covered by existing cached results (e.g., SELECT * FROM users WHERE id = 1 served immediately from a cached SELECT * FROM users)
  • Config file support in Docker — mount a TOML config file into the container via CONFIG_FILE env var or --config CLI arg

Observability

  • New pgcache.queries.allowlist_skipped metric counts queries rejected by the table allowlist
  • New pgcache.cache.subsumptions metric counts queries served via predicate subsumption
  • New pgcache.cache.subsumption_latency_seconds histogram tracks subsumption detection latency
  • CORS headers added to the metrics HTTP endpoint

Docker

  • New environment variables: CONFIG_FILE, ALLOWED_TABLES, PINNED_QUERIES, PINNED_TABLES
  • New CLI arguments: --config, --allowed-tables, --pinned-queries, --pinned-tables
  • UPSTREAM_URL is no longer required when a config file is provided

Internals

  • Dynamic publication management reduces CDC bandwidth by only subscribing to tables with active cached queries
  • EcoString used in table constraints for memory efficiency

0.4.2 — 2026-02-28

Bug Fixes

  • Fixed TLS reads losing unconsumed ciphertext across poll calls
  • Fixed TLS partial writes dropping data

Observability

  • New pgcache.cache.freshness_hits metric counts cached queries confirmed fresh (not invalidated) by each CDC event

0.4.1 — 2026-02-27

Security & Connectivity

  • ssl_mode = "require" now matches PostgreSQL semantics: encrypts the connection without verifying the server certificate, allowing connections to servers with self-signed or private CA certificates
  • New ssl_mode = "verify-full" option: encrypts the connection and verifies the server certificate against trusted public CAs (the previous behavior of require)

0.4.0 — 2026-02-27

Query Language Support

  • Correlated scalar subqueries
  • Correlated IN/ANY subqueries
  • Correlated NOT IN/ALL subqueries
  • IS TRUE, IS NOT TRUE, IS FALSE, IS NOT FALSE, IS UNKNOWN, IS NOT UNKNOWN expressions
  • Locking clauses (FOR UPDATE, FOR SHARE, etc.) are now properly forwarded to the origin as uncacheable

Performance

  • More precise CDC invalidation for DELETE events on inner join tables

Internals

  • Split ast.rs and resolved.rs into separate submodule files for maintainability
  • Refactored query dispatch, connection handling, query registration, and population streaming
  • Replaced boxed CacheableQuery with Arc-based sharing

0.3.8 — 2026-02-23

Query Language Support

  • EXISTS and NOT EXISTS subqueries

Bug Fixes

  • Fixed count(*) handling in cached function expressions

Internals

  • Refactored update query generation to work exclusively with the resolved AST
  • Reorganized transform and query update logic into a clearer file layout
  • Replaced expect() calls with proper error handling
  • Grouped related fields in ConnectionState for better organization

0.3.7 — 2026-02-22

Query Language Support

  • Predicate pushdown for subqueries and CTEs
  • Mixed wildcards and explicit columns in SELECT lists (SELECT *, id, name FROM ...)

Internals

  • Overhauled extended query protocol handling for improved Parse/Bind/Describe/Execute support

0.3.6 — 2026-02-18

Query Language Support

  • Immutable function calls in WHERE and JOIN clauses (WHERE lower(name) = 'foo') — function volatility loaded from pg_proc at startup

Performance

  • Cache admission policy — queries must be seen multiple times before being cached (configurable via admission_threshold, default 2)
  • CLOCK eviction — second-chance algorithm replaces FIFO (configurable via cache_policy)
  • Fast readmission of CDC-invalidated queries — metadata retained so re-seen queries skip the admission threshold

Observability

  • New metrics: pgcache.cache.queries_pending, pgcache.cache.queries_invalidated, pgcache.cache.readmissions
  • Improved error reporting when the metrics server fails to start

0.3.5 — 2026-02-16

Query Language Support

  • LIMIT and OFFSET clause caching — queries differing only in LIMIT/OFFSET share a cache entry, with automatic re-population when a larger limit is requested
  • BETWEEN, NOT BETWEEN, BETWEEN SYMMETRIC, and NOT BETWEEN SYMMETRIC expressions
  • ANY and ALL expressions (= ANY(ARRAY[...]), op ALL(...)) with array literals and parameters
  • LIKE, NOT LIKE, ILIKE, and NOT ILIKE pattern matching expressions

Performance

  • Inequality constraint checking for CDC invalidation — UPDATE and DELETE events on joined tables are filtered using <, <=, >, >=, and != constraints, reducing unnecessary cache invalidation
  • CDC worker sends commands directly to the writer thread, eliminating an intermediate message hop through the runtime event loop

Bug Fixes

  • NULL values in CDC update queries now carry type casts (NULL::typename) for correct PostgreSQL type inference in VALUES clauses

0.3.4 — 2026-02-12

Query Language Support

  • LEFT JOIN and RIGHT JOIN queries with equality join conditions

Performance

  • Streaming cache population (rows streamed directly instead of buffered)
  • Multi-value upserts for cache population (batched inserts instead of one per row)

Bug Fixes

  • Fixed cache table not populated properly for self-joins
  • Fixed parentheses missing when deparsing logical expressions
  • Preserved error context when bubbling errors up

Internals

  • Local connection handling for cache worker
  • Refactored integration tests to use more precise cache hit/miss detection

0.3.3 — 2026-02-09

Query Language Support

  • Uncorrelated subqueries in SELECT, FROM (derived tables), and WHERE (IN, NOT IN, scalar)
  • Nested subqueries (multi-level)
  • Non-recursive CTEs (WITH ... AS), including MATERIALIZED / NOT MATERIALIZED
  • CDC invalidation for subqueries and CTEs with directional semantics (inclusion vs exclusion vs scalar)

Observability

  • Queue depth metrics for all unbounded channels
  • Wait time metrics in worker for incoming work and database connection waits

Internals

  • Subquery nesting depth factors into query complexity scoring
  • Cache population ordering ensures inner/simpler queries are populated before outer queries that depend on them
  • Moved constraint handling from cached query into update queries
  • Improved LSN confirmation handling
  • Respect column ordering when registering a table

0.3.2 — 2026-02-05

Query Language Support

  • Set operations (UNION, INTERSECT, EXCEPT) in select statements

Internals

  • Refactored CacheQuery to use QueryExpr and ResolvedQueryExpr for set operation support

0.3.1 — 2026-02-04

Query Language Support

  • Arithmetic expressions
  • ORDER BY in aggregate functions
  • IS [NOT] NULL expressions
  • IN expressions
  • Window functions
  • CASE expressions
  • Functions in select target list
  • Subqueries in select statements
  • GROUP BY, HAVING, and LIMIT clauses

Performance

  • Split writer command queue into separate queues for query registrations and CDC handling
  • Refactored query registration to avoid blocking writer processing
  • Concurrent processing of queries when handling INSERT and UPDATE CDC messages
  • Multi-table join support with optimized invalidation

2026-01-30

Observability

  • Prometheus metrics endpoint
  • Metrics for writer receive queue sizes
  • CDC operation metrics
  • Query processing timing instrumentation

Performance

  • Cache database connection pool for worker threads
  • Pre-computed join column equivalences for invalidation (replaces AST traversal)
  • More precise invalidation for joins (skips when UPDATE doesn’t modify join columns or query constraints)

Reliability

  • Refined behavior during cache failures

2026-01-24

Features

  • Separate replication connection settings
  • Domain and enum type support in query registration
  • Log level configuration setting
  • Partitioned table support
  • Duplicate query registration prevention

Performance

  • Fixed pool of tasks for query registration
  • Generation tracking bundled with simple query

Error Handling

  • Switched to rootcause crate for location-tracking error reports
  • attach_loc() context throughout codebase

2026-01-12

Security & Connectivity

  • TLS support for client and origin connections
  • Origin database password support
  • Automatic replication slot and publication creation on startup

Bug Fixes

  • Fixed TLS session ticket handling causing connection hangs
  • Fixed select! panic on stream closure
  • PostgreSQL identifier quoting where necessary
  • Stripped SCRAM-SHA-256-PLUS from SASL options (unsupported)

2026-01-03

Cache Management

  • Cache size limit enforcement
  • Clean cache database on start/restart
  • Generation-based cache purging with row removal
  • Generation tracking for inserts and updates
  • Unlogged cache tables for performance
  • Initial cache size tracking

Reliability

  • Cache thread restart on failure detection
  • Proper CDC/cache thread failure detection

2025-12-20

Schema Support

  • Full search path support
  • Explicit schema qualification for all cache tables
  • Schema-aware table metadata lookup
  • Resolved AST with aliases and deparse

2025-12-04

Protocol Support

  • Binary parameters in extended query protocol
  • Correct column types returned from cache

Code Quality

  • Refactored integration tests
  • Split proxy.rs into modules

2025-11-12

Observability

  • Initial metrics implementation

Bug Fixes

  • Switched to sequential CDC event handling
  • Fixed CDC insert message handling

2025-10-17

Protocol Support

  • Extended query protocol support
  • Order By in queries
  • Parameter references in queries

Performance

  • Optimized cache query handling
  • DataRow message grouping
  • SIGINT handling with graceful shutdown

Query Analysis

  • Constraint propagation for cache invalidation
  • Multiple join support

2025-09-30

Query Support

  • Chained AND/OR expressions
  • Table aliases
  • VALUES clause
  • Subqueries (select target list and table position)

Internals

  • Generic nodes() iterator for AST traversal
  • Reworked CDC message support for joins

2025-08-08 — Initial Release

Core Architecture

  • Transparent PostgreSQL caching proxy
  • Simple query protocol support
  • Worker thread architecture for cache handling
  • MD5 and SCRAM authentication passthrough

Query Support

  • Query cacheability analysis
  • Initial join support
  • Comparison operators
  • Custom AST with fingerprinting and deparse

Cache Invalidation

  • CDC-based invalidation via logical replication
  • TRUNCATE message handling
  • Relation message handling (invalidates on schema change)

Configuration

  • TOML configuration file support
  • Transaction handling (queries bypass cache during transactions)