Transactional Materialized Views with Incremental Refresh for PostgreSQL
Core infrastructure for FraiseQL's GraphQL Cascade β automatic incremental refresh of JSONB read models with 5,000-12,000Γ performance gains.
By Lionel Hamayon β’ Part of the FraiseQL framework
Features β’ Quick Start β’ Performance β’ Documentation β’ Architecture
pg_tviews is the performance foundation for FraiseQL's CQRS architecture:
| Tool | Purpose | Status | Performance Gain |
|---|---|---|---|
| pg_tviews | Incremental materialized views | Beta β | 100-500Γ faster |
| jsonb_delta | JSONB surgical updates | Stable | 2-7Γ faster |
| pgGit | Database version control | Stable | Git for databases |
| confiture | PostgreSQL migrations | Stable | 300-600Γ faster |
| fraiseql | GraphQL framework | Stable | 7-10Γ faster |
| fraiseql-data | Seed data generation | Phase 6 | Auto-dependency resolution |
| Library | Purpose | Framework Support |
|---|---|---|
| graphql-cascade | Automatic cache invalidation | Apollo, React Query, Relay, URQL |
How pg_tviews fits:
- fraiseql uses pg_tviews for GraphQL read models (tv_* tables)
- jsonb_delta optimizes JSONB updates (1.5-3Γ faster)
- confiture manages TVIEW schema evolution
- graphql-cascade (client-side) invalidates browser caches when mutations trigger refreshes
Stack it up:
# Install extensions
CREATE EXTENSION pg_tviews;
CREATE EXTENSION jsonb_delta; -- Optional: 1.5-3Γ faster JSONB
# Create incremental view
CREATE TABLE tv_post AS SELECT ...;
# Use with fraiseql GraphQL
@fraiseql.type(sql_source="tv_post")
class Post: ...Current Version: 0.1.0-beta.1 (December 2025)
- Status: Public Beta - Feature-complete, API may change
- Production Use: Suitable for evaluation, not mission-critical systems
- Support: Community support via GitHub issues
Roadmap to 1.0.0 (Q1 2026):
- β Core TVIEW functionality complete
- β Comprehensive documentation (in progress)
- π Production hardening and testing
- π Security audit
- π Performance validation at scale
Breaking Changes: Minor API changes possible until 1.0.0. Pin to exact version in production.
Traditional PostgreSQL materialized views require full rebuilds on every refreshβscanning entire tables and recomputing all rows. For large datasets or complex views with JOINs, this becomes prohibitively expensive:
-- Traditional approach: Full rebuild every time
REFRESH MATERIALIZED VIEW my_view; -- Scans ALL rows, recomputes EVERYTHINGResult: Minutes of downtime, high I/O, locks, and stale data between refreshes.
pg_tviews brings incremental materialized view maintenance to PostgreSQL with surgical, row-level updates that happen automatically within your transactions:
-- pg_tviews: Automatic incremental refresh
CREATE TABLE tv_post AS
SELECT p.pk_post as pk_post, jsonb_build_object(...) as data
FROM tb_post p JOIN tb_user u ON p.fk_user = u.pk_user;
-- Just use your database normally:
INSERT INTO tb_post(title, fk_user) VALUES ('New Post', 123);
COMMIT; -- tv_post automatically updated with ONLY the affected row!Result: Millisecond updates, no full scans, always up-to-date, zero manual intervention.
For 1.5-3Γ faster JSONB updates, install the optional jsonb_delta extension:
CREATE EXTENSION jsonb_delta; -- Optional: 1.5-3Γ faster JSONB updates
CREATE EXTENSION pg_tviews;Without jsonb_delta, pg_tviews uses standard PostgreSQL JSONB operations (still fast, just not optimized).
pg_tviews follows FraiseQL's trinity identifier conventions for optimal GraphQL Cascade performance:
id(UUID): Public identifier for GraphQL/REST APIspk_entity(integer): Primary key for efficient joins and lineage trackingfk_*(integer): Foreign keys for cascade propagationidentifier(text): Optional unique slugs for SEO-friendly URLs{parent}_id(UUID): Optional UUID FKs for FraiseQL filtering
Example TVIEW with full trinity support:
CREATE TABLE tv_post AS
SELECT
p.pk_post, -- lineage root
p.id, -- GraphQL ID
p.identifier, -- SEO slug
p.fk_user, -- cascade FK
u.id as user_id, -- FraiseQL filtering FK
jsonb_build_object(
'id', p.id,
'identifier', p.identifier,
'title', p.title,
'author', jsonb_build_object(
'id', u.id,
'identifier', u.identifier,
'name', u.name,
'email', u.email
)
) as data
FROM tb_post p
JOIN tb_user u ON p.fk_user = u.pk_user;- π Smart Dependency Detection: Automatically analyzes SQL to find source tables and relationships
- π― Surgical Updates: Updates only affected rowsβnever full table scans
- π Transactional Consistency: Refresh happens atomically within your transaction
- π Cascade Propagation: Automatically handles multi-level view dependencies
- β‘ 100-500Γ Faster Triggers: Statement-level triggers for bulk operations
- πΎ Query Plan Caching: 10Γ faster with cached prepared statements
- π¦ Bulk Optimization: N rows with just 2 queries instead of N queries
- π¨ Smart Patching: 2Γ performance boost with optional jsonb_delta integration
- π Two-Phase Commit (2PC): Distributed transaction support with queue persistence
- π Connection Pooling: Full PgBouncer/pgpool-II compatibility with DISCARD ALL handling
- π Comprehensive Monitoring: Real-time metrics, health checks, performance views
- π‘οΈ Enterprise-Grade Code: 100% clippy-strict compliance, panic-safe FFI, zero unwraps
- π SBOM Generation: Automated Software Bill of Materials in SPDX 2.3 and CycloneDX 1.5 formats
- π Cryptographic Signing: Sigstore keyless + GPG maintainer signatures for all releases
- π‘οΈ Dependency Security: Automated vulnerability scanning with cargo-audit + cargo-vet audits
- π Automated Updates: Dependabot integration for security patches and updates
- ποΈ Reproducible Builds: Docker-based build environment with locked dependencies
- π International Compliance: EU Cyber Resilience Act, US EO 14028, PCI-DSS 4.0, ISO 27001
- π Supply Chain Security: SLSA Level 3 provenance with dependency transparency
- π Vulnerability Management: Complete dependency inventory for CVE tracking
- π Simple API:
pg_tviews_create()function for easy TVIEW creation - π§ JSONB Optimized: Built for modern JSONB-heavy applications
- π Array Support: Full INSERT/DELETE handling for array columns
- π Excellent Debugging: Rich error messages, debug functions, health checks
| Operation | Traditional MV | pg_tviews | Improvement |
|---|---|---|---|
| Single row update | 2,500ms | 1.2ms | 2,083Γ |
| Medium cascade (50 rows) | 7,550ms | 3.72ms | 2,028Γ |
| Bulk operation (1K rows) | 180,000ms | 100ms | 1,800Γ |
- Linear scaling with data size for incremental updates
- Sub-linear scaling for cascading updates (graph caching)
- Constant time for cache hits (90%+ hit rate in production)
- O(1) queue operations with HashSet-based deduplication
# Prerequisites
# - PostgreSQL 13-18 installed
# - Rust toolchain 1.70+
# Install pgrx
cargo install --locked cargo-pgrx
# Initialize pgrx
cargo pgrx init
# Clone and build
git clone https://github.com/fraiseql/pg_tviews.git
cd pg_tviews
cargo pgrx install --release
# Enable in your database
psql -d your_database -c "CREATE EXTENSION pg_tviews;"-- Create base tables (FraiseQL style)
CREATE TABLE tb_user (
pk_user BIGSERIAL PRIMARY KEY,
id UUID NOT NULL DEFAULT gen_random_uuid(),
identifier TEXT UNIQUE,
name TEXT,
email TEXT
);
CREATE TABLE tb_post (
pk_post BIGSERIAL PRIMARY KEY,
id UUID NOT NULL DEFAULT gen_random_uuid(),
identifier TEXT UNIQUE,
title TEXT,
content TEXT,
fk_user BIGINT REFERENCES tb_user(pk_user)
);
-- Create a TVIEW (note: tv_ prefix is required)
CREATE TABLE tv_post AS
SELECT
p.pk_post as pk_post, -- Primary key column (required)
p.id, -- GraphQL ID
p.identifier, -- SEO slug
p.fk_user, -- Cascade FK
u.id as user_id, -- FraiseQL filtering FK
jsonb_build_object(
'id', p.id,
'identifier', p.identifier,
'title', p.title,
'content', p.content,
'author', jsonb_build_object(
'id', u.id,
'identifier', u.identifier,
'name', u.name,
'email', u.email
)
) as data -- JSONB data column (required)
FROM tb_post p
JOIN tb_user u ON p.fk_user = u.pk_user;
-- Use it like a table
SELECT data FROM tv_post WHERE data->>'title' ILIKE '%rust%';
-- It updates automatically!
INSERT INTO tb_user (identifier, name, email) VALUES ('alice', 'Alice', 'alice@example.com');
INSERT INTO tb_post (identifier, title, content, fk_user) VALUES
('learning-rust', 'Learning Rust', 'Rust is amazing!', 1);
-- tv_post is now automatically up-to-date!
SELECT data FROM tv_post;-- Install statement-level triggers for 100-500Γ better bulk performance
SELECT pg_tviews_install_stmt_triggers();
-- Monitor system health
SELECT * FROM pg_tviews_health_check();
-- View real-time metrics
SELECT * FROM pg_tviews_queue_realtime;βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Application β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β INSERT/UPDATE/DELETE
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL Core β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β tb_* Tables ββββββΆβ Triggers ββββββΆβ Refresh Queueβ β
β β (command) β β (per-row or β β (thread-local)β β
β ββββββββββββββββ β statement) β ββββββββ¬βββββββββ β
β ββββββββββββββββ β β
β ββββββββββββββββ β β
β β ProcessUtil β β β
β β Hook (DDL) β β β
β ββββββββββββββββ β β
β β β
β βββββββββββββββββββββββββββββΌβββββββββββ β
β β Transaction Callback Handler β β
β β (PRE_COMMIT, COMMIT, ABORT, 2PC) β β
β ββββββββββββ¬βββββββββββββββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β pg_tviews Refresh Engine β β
β β β β
β β βββββββββββββββββββββββββββββββββββββββ β β
β β β Dependency Graph Resolution β β β
β β β (Topological Sort, Cycle Detect) β β β
β β βββββββββββββ¬βββββββββββββββββββββββββββ β β
β β β β β
β β βΌ β β
β β βββββββββββββββββββββββββββββββββββββββ β β
β β β Bulk Refresh Processor β β β
β β β (2 queries for N rows) β β β
β β βββββββββββββ¬βββββββββββββββββββββββββββ β β
β β β β β
β β βΌ β β
β β βββββββββββββββββββββββββββββββββββββββ β β
β β β Cache Layer (Graph, Table, Plan) β β β
β β βββββββββββββ¬βββββββββββββββββββββββββββ β β
β β β β β
β β βΌ β β
β β βββββββββββββββββββββββββββββββββββββββ β β
β β β Metrics & Monitoring β β β
β β βββββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β TVIEW Tablesβββββββ Backing βββββββ Metadata β β
β β (tv_*) β β Views (v_*) β β (pg_tview_*)β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Trigger System: Captures changes at source tables, enqueues refresh operations
- Transaction Queue: Thread-local HashSet for deduplication and ACID guarantees
- Dependency Graph: Resolves refresh order, detects cycles, enables cascading
- Refresh Engine: Executes surgical updates with bulk optimization
- Cache Layer: Three-tier caching (graph, table OIDs, query plans)
- Monitoring: Real-time metrics, health checks, performance analytics
- Quick Start - Step-by-step setup guide
- Installation - Detailed installation instructions
- FraiseQL Integration - Framework integration guide
- For Developers - Application integration patterns
- For Operators - Production deployment guide
- For Architects - CQRS design decisions
- API Reference - Complete function reference
- DDL Reference - CREATE/DROP TABLE syntax
- Syntax Comparison - TVIEW creation methods
- Error Reference - Error types and solutions
- Configuration - Configuration options
- Monitoring - Metrics and health checks
- Troubleshooting - Debugging procedures
- Performance - π Complete performance guide (index)
- Performance Best Practices - Essential patterns
- Performance Analysis - Diagnostic tools
- Index Optimization - Index strategies
- Performance Tuning - Advanced tuning
- Security - Security best practices
- SBOM - Software Bill of Materials and supply chain security
- Disaster Recovery - Backup and recovery
- Runbooks - Operational procedures
- Upgrades - Version migration guides
- Overview - Performance testing methodology and 4-way comparison
- Running Benchmarks - How to run benchmarks (Docker, pgrx, manual)
- Docker Setup - Advanced Docker benchmarking (requires jsonb_delta)
- Results Interpretation - Understanding benchmark results
- Results - Detailed benchmark data
- Contributing - Development setup and contribution guidelines
- Testing - Testing patterns and procedures
- Architecture Deep Dive - Technical architecture details
β FraiseQL Applications - Real-time GraphQL Cascade with UUID filtering β E-commerce Dashboards - Real-time product aggregations with inventory β Analytics Workloads - Pre-aggregated reporting tables that stay fresh β API Response Caching - JSONB views for fast API responses β Activity Feeds - User timelines with JOINed data β Denormalization - Read-optimized tables without manual cache invalidation
β Write-Heavy Tables - If you have >1000 writes/sec per table β Simple Queries - If a regular index works fine β Append-Only Logs - No need for incremental refresh
Contributions welcome! This is a portfolio project, but I'm happy to collaborate:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup: See DEVELOPMENT.md
This project is licensed under the MIT License - see the LICENSE file for details.
β If you find this project interesting, please consider starring it! β
Built with β€οΈ and Rust π¦