# v0.59.0 — Performance & Observability

> **Full details:** [v0.59.0.md-full.md](v0.59.0.md-full.md)

## What's New

v0.59.0 closes the MEDIUM-severity performance and observability gaps
identified in the v0.57.0 overall assessment
([plans/PLAN_OVERALL_ASSESSMENT_12.md](../plans/PLAN_OVERALL_ASSESSMENT_12.md)).
It eliminates several hot-path allocations and SPI fan-outs, and adds the
Prometheus metrics that SREs need to operate stream tables confidently in
production.

### PERF-1: Batched Monitor Buffer-Growth Check (P-1)

The health-check loop in `src/monitor/health.rs` previously issued one
`SELECT count(*)` SPI call per CDC-enabled source table.  On a cluster with
100 CDC sources at a 10 Hz monitor tick, this produced ~1 000 SPI round-trips
per second.  The check is now batched into a single SPI query using a
`VALUES` lateral union, reducing the cost from O(N × ticks) to O(ticks).

### PERF-2: Query Hash Cached on StreamTableMeta (P-2)

`defining_query_hash` is computed once at CREATE/ALTER time and stored in
`pgtrickle.pgt_stream_tables`.  The per-refresh hash computation
(`DefaultHasher` over the full query string) is eliminated.  The SQL upgrade
script adds the new column and backfills it for all existing stream tables.

### PERF-3: Arc<str> Merge Template Strings (P-3)

The eight SQL template strings inside `CachedMergeTemplate` are now stored
as `Arc<str>`.  Cache-hit clones become cheap atomic reference-count
increments instead of multi-kilobyte `memcpy` operations.

### PERF-4: Single MERGE_TEMPLATE_CACHE Borrow per Refresh (P-4)

`cached_non_monotonic` and `cached_is_deduplicated` are now extracted in a
single `RefCell::borrow()` call, preventing the second borrow from distorting
LRU ordering and halving the number of cache-lock acquisitions per refresh.

### PERF-5: WAL Decoder UPDATE Path Vec Pre-allocation (P-5)

The WAL-decoder UPDATE handler now calls `Vec::with_capacity(num_columns)`
before the column loop and uses `write!` into a single pre-allocated `String`
for placeholder construction, eliminating the five separate `Vec<String>`
allocations (plus multiple `to_string()` calls) per UPDATE row.

### PERF-6: Frontier Borrow Instead of Clone (P-6)

`has_stream_table_source_changes()` now borrows `st.frontier` via
`as_ref().unwrap_or(&Frontier::EMPTY)` instead of cloning the entire
`Frontier` struct (which contains a `HashMap` of upstream LSN entries) on
every upstream-change probe.

### PERF-7: Diamond Detection — Lazy Intersection (P-7)

The pair-wise branch-ancestor intersection in `detect_diamonds()` now uses a
lazy `intersection()` iterator that short-circuits on the first shared node,
avoiding the `collect()` into a temporary `Vec` for every branch pair.

### OBS-1: CDC Lag Percentile Metrics (O-1)

Three new Prometheus gauges expose CDC-lag distribution across all live
stream tables:

- `pg_trickle_cdc_lag_p50_seconds` — median lag
- `pg_trickle_cdc_lag_p95_seconds` — 95th percentile lag
- `pg_trickle_cdc_lag_p99_seconds` — 99th percentile lag

Percentiles are computed from a rolling 15-minute sample window stored in
shared memory and updated on every frontier advance.

### OBS-2: Parallel Worker Queue-Depth and Idle-Time Metrics (O-2)

Two new Prometheus counters expose parallel worker utilisation:

- `pg_trickle_parallel_queue_depth` — number of refresh jobs waiting for a
  worker
- `pg_trickle_worker_idle_time_seconds_total` — cumulative time workers have
  spent waiting for work

### OBS-3: WAL Decoder Pending-Record Metric (O-3)

`pg_trickle_wal_decoder_pending_records` exposes the number of logical
replication records buffered but not yet written to the CDC change buffer.
A persistent non-zero value indicates the decoder is falling behind and
allows operators to alert before the replication slot runs out of WAL.

### OBS-4: Refresh-Mode Ratio Metric per Stream Table (O-4)

Two new Prometheus counters track mode selection per stream table:

- `pg_trickle_refresh_mode_total{mode="differential",pgt_id="…"}` — count of
  DIFFERENTIAL refresh cycles
- `pg_trickle_refresh_mode_total{mode="full",pgt_id="…"}` — count of FULL
  refresh cycles

These counters let operators verify that the adaptive cost model is keeping
queries on the differential hot path and detect unexpected fallback patterns.

### OBS-5: Background Worker application_name Tagging (O-5)

All background worker connections now call
`SET application_name = 'pg_trickle_<role>'` immediately after connecting.
Scheduler, dispatcher, pool worker, and WAL decoder processes are now
individually identifiable in `pg_stat_activity`, `pg_stat_bgwriter`, and
`pg_locks`.

### OBS-6: Backup & Restore Documentation (O-6)

`INSTALL.md` gains a new "Backup & Restore" section documenting the required
`pg_dump` flags to include both the `pgtrickle` catalog schema and the
`pgtrickle_changes` change-buffer schema.  The section also covers
post-restore validation using `pgtrickle.health_check()`.
