# Architecture And Tradeoffs
This page exists so you can roast our architecture decisions. Tell us why a
tradeoff is wrong, what we're misinformed about, and what you'd do instead.
We appreciate brutal feedback. The goal is to make the product better.
The architecture is still open to critique. This page explains the main design
choices, the tradeoffs behind them, and the current pressure points that may prove
a different direction is better.
The short version: pgGraph is a derived graph execution layer for existing
PostgreSQL tables. PostgreSQL remains the source of truth. pgGraph builds a
rebuildable graph artifact and uses compact in-memory structures to answer
bounded traversal, path, and relationship queries through SQL.
It is not a new PostgreSQL storage engine, not a replacement for PostgreSQL's
buffer pool, and not a separate graph database that owns your data.
## Design Philosophy
| Decision | Tradeoff |
|---|---|
| Keep source tables authoritative | pgGraph can rebuild from PostgreSQL data, but query speed depends on a derived artifact being fresh enough for the workload. |
| Use SQL functions as the public API | Applications stay inside PostgreSQL; `graph.gql()` exposes the bounded documented GQL profile rather than claiming full language conformance. |
| Precompute CSR adjacency | Traversal avoids repeatedly discovering edges through joins, but builds and maintenance become explicit operational steps. |
| Keep engines backend-local | This matches PostgreSQL's process model and avoids shared mutable Rust state, but each backend still owns its mapping handles, bounded decoded metadata, and mutable overlays. |
| Load artifacts into immutable snapshots | Linux backends can share sealed snapshot pages; unavailable sharing falls back to a private anonymous snapshot. Each backend still validates its artifact and accounts for the full logical base size. |
| Enforce circuit breakers | Traversal is bounded for database safety, but pgGraph is not trying to run unbounded graph analytics in OLTP query paths. |
## Why mmap?
Database engineers are right to be suspicious of mmap. The CMU Database Group's
CIDR 2022 paper
[Are You Sure You Want to Use MMAP in Your Database Management System?](https://db.cs.cmu.edu/mmap-cidr2022/)
argues that mmap is not a suitable replacement for a traditional DBMS buffer
pool. That warning is relevant, and pgGraph should be evaluated against it.
pgGraph uses read-only mappings of immutable, rebuildable graph artifact
snapshots. Linux can share sealed snapshots across backends; other platforms
and unavailable sharing use private anonymous snapshots.
When `graph.persist_on_build = true`, pgGraph writes a `.pggraph` artifact from
registered PostgreSQL tables. Later backend processes load and validate an
immutable snapshot containing:
- node active bits, table OIDs, primary-key offsets, and primary-key bytes;
- forward and inbound CSR offsets, targets, edge-label IDs, optional weights,
schema-direction flags, and relationship IDs;
- the resolution index used to map source table coordinates to graph node IDs;
- registered filter values and lexical text dictionaries;
- relationship identity descriptors and source-key bytes.
The snapshot keeps fixed-width data compact and prevents another process from
invalidating typed Rust views by writing or truncating the source inode. The
Linux sharing path copies bytes into a sealed memory file and advertises a
validated handle for other backends to adopt. Seals prohibit writes, shrinking,
and growth. Cache hints do not authorize a graph or replace artifact validation.
When sharing is unavailable, loading falls back to a private anonymous copy.
Backend metadata and sync overlays remain private. Logical memory accounting
charges the full base to each backend even when physical pages are shared; the
shared-byte diagnostics do not measure cluster-wide physical memory use.
The boundary matters:
- PostgreSQL still owns table storage, WAL, MVCC, indexes, durability, crash
recovery, ACLs, RLS, backups, and application writes.
- The `.pggraph` file is derived state. If it is missing, incompatible, or
corrupt, rebuild it from source tables.
- pgGraph snapshots artifact sections read-only. Sync overlays and mutable derived
state remain backend-local.
- Filter mutations and relationship identities added after load remain compact
backend-local overlays. Edge-label and filter-column descriptors are bounded
decoded metadata rather than graph-sized copies.
That does not make mmap free. It still means pgGraph must account for page
faults, OS eviction decisions, file integrity, artifact validation, memory
observability, and platform-specific behavior. The design is a narrow use of
mmap for immutable derived data, not a claim that mmap is a general-purpose
database buffer manager.
## Why Not Just SQL/PGQ?
PostgreSQL 19 is expected to introduce SQL/PGQ: `CREATE PROPERTY GRAPH`,
`GRAPH_TABLE`, and a standard way to express graph patterns inside SQL. SQL/PGQ
gives PostgreSQL a standards-based graph query surface backed by the planner and
optimizer — the same infrastructure that makes PostgreSQL's relational queries
strong.
pgGraph solves a different problem at a different layer. SQL/PGQ expresses graph
patterns and lets the PostgreSQL optimizer choose how to execute them. pgGraph
precomputes a CSR adjacency layout from registered tables so that repeated
bounded traversals over known topology avoid rediscovering relationships through
relational joins on every query. The tradeoff is that pgGraph requires explicit
build and maintenance steps to keep that derived structure fresh.
The long-term fit may be complementary:
- SQL/PGQ can become the natural way to express graph queries in PostgreSQL.
- pgGraph can act as a specialized runtime or graph-index-like structure for the
subset of patterns that match its bounded traversal model.
- General graph patterns should continue to use PostgreSQL's relational planning
and execution path when that is the better fit.
Today that complement is represented only as an internal typed adapter seam.
pgGraph does not parse `GRAPH_TABLE` SQL text or expose a public SQL/PGQ API;
it can lower eligible PostgreSQL-owned graph-pattern shapes into the shared IR
once PostgreSQL exposes stable hooks for that handoff.
## Where We May Be Wrong
pgGraph is young. These are the areas where critique is especially useful:
- Whether read-only mmap remains the right artifact-loading strategy at larger
graph sizes and higher backend counts.
- Whether conditional Linux snapshot sharing provides enough memory savings
at high backend counts, and whether the private fallback remains practical
on other platforms and under resource pressure.
- Whether planner integration should become deeper than conservative function
`COST` and `ROWS` hints. Possible directions include custom scan nodes that
let the PostgreSQL planner push predicates into graph traversal, path-key
integration for merge joins over graph results, or statistics-based cost
estimation that reflects actual graph topology rather than static row-count
estimates.
- Whether SQL/PGQ should become the main public query surface sooner, with
pgGraph acting mostly as a runtime for eligible patterns.
- Whether operational complexity around build, sync, maintenance, and artifact
freshness is acceptable for the workloads pgGraph targets.
We welcome architecture feedback, benchmark results, failure reports, and
counterexamples. The most useful critiques are specific: workload shape, graph
size, PostgreSQL version, query pattern, freshness requirement, memory budget,
and what behavior would make pgGraph safer or easier to operate.