# Architecture And Tradeoffs

<Callout type="tip">
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.
</Callout>

The architecture is still open to critique. This page explains the main design
choices, the tradeoffs behind them, and the places where future work 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 mapped snapshot, bounded decoded metadata, and mutable overlays. |
| Copy artifacts into read-only anonymous mappings | Typed views get stable immutable bytes even if the source inode changes, at the cost of one artifact-sized private snapshot per loading backend. |
| 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?

<Callout type="warning">
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.
</Callout>

pgGraph uses anonymous read-only mappings for immutable, rebuildable graph
artifact snapshots, not as a mutable database storage engine or PostgreSQL
buffer-pool replacement.

When `graph.persist_on_build = true`, pgGraph writes a `.pggraph` artifact from
registered PostgreSQL tables. Later backend processes copy and validate that
artifact in an anonymous read-only mapping:

- 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
tradeoff is that each loading backend owns one artifact-sized snapshot instead
of sharing file-backed pages across backends.

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 anonymous per-backend snapshots remain preferable to a different
  shared representation now that both CSR directions and graph-sized metadata
  use mmap-friendly sections.
- 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.
