> **Plain-language companion:** [v0.7.0.md](v0.7.0.md) ## v0.7.0 — Performance, Watermarks, Circular DAG Execution, Observability & Infrastructure **Status: Released (2026-03-16).** **Goal:** Land Part 9 performance improvements (parallel refresh scheduling, MERGE strategy optimization, advanced benchmarks), add user-injected temporal watermark gating for batch-ETL coordination, complete the fixpoint scheduler for circular stream table DAGs, ship ready-made Prometheus/Grafana monitoring, and prepare the 1.0 packaging and deployment infrastructure. ### Watermark Gating > **In plain terms:** A scheduling control for ETL pipelines where multiple > source tables are populated by separate jobs that finish at different > times. For example, `orders` might be loaded by a job that finishes at > 02:00 and `products` by one that finishes at 03:00. Without watermarks, > the scheduler might refresh a stream table that joins the two at 02:30, > producing a half-complete result. Watermarks let each ETL job declare "I'm > done up to timestamp X", and the scheduler waits until all sources are > caught up within a configurable tolerance before proceeding. Let producers signal their progress so the scheduler only refreshes stream tables when all contributing sources are aligned within a configurable tolerance. The primary use case is nightly batch ETL pipelines where multiple source tables are populated on different schedules. | Item | Description | Effort | Ref | |------|-------------|--------|-----| | ~~WM-1~~ | ~~Catalog: `pgt_watermarks` table (`source_relid`, `current_watermark`, `updated_at`, `wal_lsn_at_advance`); `pgt_watermark_groups` table (`group_name`, `sources`, `tolerance`)~~ | ✅ Done | [PLAN_WATERMARK_GATING.md](plans/sql/PLAN_WATERMARK_GATING.md) | | ~~WM-2~~ | ~~`advance_watermark(source, watermark)` — monotonicity check, store LSN alongside watermark, lightweight scheduler signal~~ | ✅ Done | [PLAN_WATERMARK_GATING.md](plans/sql/PLAN_WATERMARK_GATING.md) | | ~~WM-3~~ | ~~`create_watermark_group(name, sources[], tolerance)` / `drop_watermark_group()`~~ | ✅ Done | [PLAN_WATERMARK_GATING.md](plans/sql/PLAN_WATERMARK_GATING.md) | | ~~WM-4~~ | ~~Scheduler pre-check: evaluate watermark alignment predicate; skip + log `SKIP(watermark_misaligned)` if not aligned~~ | ✅ Done | [PLAN_WATERMARK_GATING.md](plans/sql/PLAN_WATERMARK_GATING.md) | | ~~WM-5~~ | ~~`watermarks()`, `watermark_groups()`, `watermark_status()` introspection functions~~ | ✅ Done | [PLAN_WATERMARK_GATING.md](plans/sql/PLAN_WATERMARK_GATING.md) | | ~~WM-6~~ | ~~E2E tests: nightly ETL, micro-batch tolerance, multiple pipelines, mixed external+internal sources~~ | ✅ Done | [PLAN_WATERMARK_GATING.md](plans/sql/PLAN_WATERMARK_GATING.md) | > **Watermark gating: ✅ Complete** ### Circular Dependencies — Scheduler Integration > **In plain terms:** Completes the circular DAG work started in v0.6.0. > When stream tables reference each other in a cycle (A → B → A), the > scheduler now runs them repeatedly until the result stabilises — no more > changes flowing through the cycle. This is called "fixpoint iteration", > like solving a system of equations by re-running it until the numbers stop > moving. If it doesn't converge within a configurable number of rounds > (default 100) it surfaces an error rather than looping forever. Completes the SCC foundation from v0.6.0 with a working fixpoint iteration loop. Stream tables in a monotone cycle are refreshed repeatedly until convergence (zero net change) or `max_fixpoint_iterations` is exceeded. | Item | Description | Effort | Ref | |------|-------------|--------|-----| | ~~CYC-5~~ | ~~Scheduler fixpoint iteration: `iterate_to_fixpoint()`, convergence detection from `(rows_inserted, rows_deleted)`, non-convergence → `ERROR` status~~ | ✅ Done | [PLAN_CIRCULAR_REFERENCES.md](plans/sql/PLAN_CIRCULAR_REFERENCES.md) Part 5 | | ~~CYC-6~~ | ~~Creation-time validation: allow monotone cycles when `allow_circular=true`; assign `scc_id`; recompute SCCs on `drop_stream_table`~~ | ✅ Done | [PLAN_CIRCULAR_REFERENCES.md](plans/sql/PLAN_CIRCULAR_REFERENCES.md) Part 6 | | ~~CYC-7~~ | ~~Monitoring: `scc_id` + `last_fixpoint_iterations` in views; `pgtrickle.pgt_scc_status()` function~~ | ✅ Done | [PLAN_CIRCULAR_REFERENCES.md](plans/sql/PLAN_CIRCULAR_REFERENCES.md) Part 7 | | ~~CYC-8~~ | ~~Documentation + E2E tests (`e2e_circular_tests.rs`): 6 scenarios (monotone cycle, non-monotone reject, convergence, non-convergence→ERROR, drop breaks cycle, `allow_circular=false` default)~~ | ✅ Done | [PLAN_CIRCULAR_REFERENCES.md](plans/sql/PLAN_CIRCULAR_REFERENCES.md) Part 8 | > **Circular dependencies subtotal: ~19 hours** ### Last Differential Mode Gaps > **In plain terms:** Three query patterns that previously fell back to `FULL` > refresh in `AUTO` mode — or hard-errored in explicit `DIFFERENTIAL` mode > — despite the DVM engine having the infrastructure to handle them. > All three gaps are now closed. | Item | Description | Effort | Ref | |------|-------------|--------|-----| | ~~DG-1~~ | ~~**User-Defined Aggregates (UDAs).** PostGIS (`ST_Union`, `ST_Collect`), pgvector vector averages, and any `CREATE AGGREGATE` function are rejected. Fix: classify unknown aggregates as `AggFunc::UserDefined` and route them through the existing group-rescan strategy — no new delta math required.~~ | ✅ Done | [PLAN_LAST_DIFFERENTIAL_GAPS.md](plans/sql/PLAN_LAST_DIFFERENTIAL_GAPS.md) §G1 | | ~~DG-2~~ | ~~**Window functions nested in expressions.** `RANK() OVER (...) + 1`, `CASE WHEN ROW_NUMBER() OVER (...) <= 10`, `COALESCE(LAG(v) OVER (...), 0)` etc. are rejected.~~ | ✅ Done (v0.6.0) | [PLAN_LAST_DIFFERENTIAL_GAPS.md](plans/sql/PLAN_LAST_DIFFERENTIAL_GAPS.md) §G2 | | ~~DG-3~~ | ~~**Sublinks in deeply nested OR.** The two-stage rewrite pipeline handles flat `EXISTS(...) OR …` and `AND(EXISTS OR …)` but gives up on multiple OR+sublink conjuncts. Fix: expand all OR+sublink conjuncts in AND to a cartesian product of UNION branches with a 16-branch explosion guard.~~ | ✅ Done | [PLAN_LAST_DIFFERENTIAL_GAPS.md](plans/sql/PLAN_LAST_DIFFERENTIAL_GAPS.md) §G3 | > **Last differential gaps: ✅ Complete** ### Pre-1.0 Infrastructure Prep > **In plain terms:** Three preparatory tasks that make the eventual 1.0 > release smoother. A draft Docker Hub image workflow (tests the build but > doesn't publish yet); a PGXN metadata file so the extension can eventually > be installed with `pgxn install pg_trickle`; and a basic CNPG integration > test that verifies the extension image loads correctly in a CloudNativePG > cluster. None of these ship user-facing features — they're CI and > packaging scaffolding. | Item | Description | Effort | Ref | |------|-------------|--------|---------| | ~~INFRA-1~~ | ~~**Prove the Docker image builds.** Set up a CI workflow that builds the official Docker Hub image (PostgreSQL 18 + pg_trickle pre-installed), runs a smoke test (create extension, create a stream table, refresh it), but doesn't publish anywhere yet. When 1.0 arrives, publishing is just flipping a switch.~~ | 5h | ✅ Done | | ~~INFRA-2~~ | ~~**Publish an early PGXN testing release.** Draft `META.json` and upload a `release_status: "testing"` package to PGXN so `pgxn install pg_trickle` works for early adopters now. PGXN explicitly supports pre-stable releases; this gets real-world install testing and establishes registry presence before 1.0. At 1.0 the only change is flipping `release_status` to `"stable"`.~~ | 2–3h | ✅ Done | | ~~INFRA-3~~ | ~~**Verify Kubernetes deployment works.** A CI smoke test that deploys the pg_trickle extension image into a CloudNativePG (CNPG) Kubernetes cluster, creates a stream table, and confirms a refresh cycle completes. Catches packaging and compatibility issues before they reach Kubernetes users.~~ | 4h | ✅ Done | > **Pre-1.0 infrastructure prep: ✅ Complete** ### Performance — Regression Fixes & Benchmark Infrastructure (Part 9 S1–S2) ✅ Done > Fixes Criterion benchmark regressions identified in Part 9 and ships five > benchmark infrastructure improvements to support data-driven performance > decisions. | Item | Description | Status | |------|-------------|--------| | A-3 | Fix `prefixed_col_list/20` +34% regression — eliminate intermediate `Vec` allocation | ✅ Done | | A-4 | Fix `lsn_gt` +22% regression — use `split_once` instead of `split().collect()` | ✅ Done | | I-1c | `just bench-docker` target for running Criterion inside Docker builder image | ✅ Done | | I-2 | Per-cycle `[BENCH_CYCLE]` CSV output in E2E benchmarks for external analysis | ✅ Done | | I-3 | EXPLAIN ANALYZE capture mode (`PGS_BENCH_EXPLAIN=true`) for delta query plans | ✅ Done | | I-6 | 1M-row benchmark tier (`bench_*_1m_*` + `bench_large_matrix`) | ✅ Done | | I-8 | Criterion noise reduction (`sample_size(200)`, `measurement_time(10s)`) | ✅ Done | ### Performance — Parallel Refresh, MERGE Optimization & Advanced Benchmarks (Part 9 S4–S6) ✅ Done > DAG level-parallel scheduling, improved MERGE strategy selection (xxh64 > hashing, aggregate saturation bypass, cost-based threshold), and expanded > benchmark suite (JSON comparison, concurrent writers, window/lateral/CTE). | Item | Description | Status | |------|-------------|--------| | C-1 | DAG level extraction (`topological_levels()` on `StDag` and `ExecutionUnitDag`) | ✅ Done | | C-2 | Level-parallel dispatch (existing `parallel_dispatch_tick` infrastructure sufficient) | ✅ Done | | C-3 | Result communication (existing `SchedulerJob` + `pgt_refresh_history` sufficient) | ✅ Done | | D-1 | xxh64 hash-based change detection for wide tables (≥50 cols) | ✅ Done | | D-2 | Aggregate saturation FULL bypass (changes ≥ groups → FULL) | ✅ Done | | D-3 | Cost-based strategy selection from `pgt_refresh_history` data | ✅ Done | | I-4 | Cross-run comparison tool (`just bench-compare`, JSON output) | ✅ Done | | I-5 | Concurrent writer benchmarks (1/2/4/8 writers) | ✅ Done | | I-7 | Window / lateral / CTE / UNION ALL operator benchmarks | ✅ Done | > **v0.7.0 total: ~59–62h** **Exit criteria:** - [x] Part 9 performance: DAG levels, xxh64 hashing, aggregate saturation bypass, cost-based threshold, advanced benchmarks - [x] `advance_watermark` + scheduler gating operational; ETL E2E tests pass - [x] Monotone circular DAGs converge to fixpoint; non-convergence surfaces as `ERROR` - [x] UDAs, nested window expressions, and deeply nested OR+sublinks supported in DIFFERENTIAL mode - [x] Docker Hub image CI workflow builds and smoke-tests successfully - [x] PGXN `testing` release uploaded; `pgxn install pg_trickle` works - [x] CNPG integration smoke test passes in CI - [x] Extension upgrade path tested (`0.6.0 → 0.7.0`) ---