Skip to content

trickle-labs/pg-eddy

pg_eddy

CI Status License: Apache 2.0 PostgreSQL 18+ Rust 1.85+ OpenCypher TCK

A PostgreSQL 18 extension implementing a high-performance native Labelled Property Graph (LPG) store via a custom Table Access Method, enabling graph queries directly inside PostgreSQL without a separate database.

Overview

pg_eddy delivers adjacency-aware graph storage inside PostgreSQL. Instead of storing graph data as heap tables and scanning B-tree indexes on every hop, pg_eddy uses a custom AM that places node adjacency information adjacent to node data on disk — enabling O(degree) per-hop traversal without index lookups.

Why pg_eddy over Neo4j or Apache AGE?

  • One system to operate: single backup, single monitoring stack, single connection pool
  • Full ACID transactions: spanning both graph and relational data in the same transaction
  • Faster multi-hop queries: adjacency-follow design is 2–5× faster than AGE on MATCH patterns
  • Incremental view maintenance: optional integration with pg-trickle for incrementally-maintained graph views

Status

For a detailed feature breakdown and release history, see CHANGELOG.md. For the complete roadmap with phases, exit criteria, and design rationale, see plans/implementation_plan.md.

Installation

Prerequisites

  • PostgreSQL 18.x
  • Rust 1.85+ (with cargo)
  • cargo-pgrx 0.18

Build from Source

# Clone the repository
git clone https://github.com/trickle-labs/pg-eddy.git
cd pg-eddy

# Install cargo-pgrx (if not already installed)
OPENSSL_NO_VENDOR=1 cargo install cargo-pgrx --version 0.18.0 --locked

# Initialize pgrx with your PostgreSQL 18 installation
cargo pgrx init --pg18 /usr/lib/postgresql/18/bin/pg_config

# Build the extension
cd pg_eddy
cargo build --features pg18

# Run tests
cargo pgrx test pg18

# Install into your PostgreSQL instance
cargo pgrx install --release --features pg18

Configuration

Add to postgresql.conf:

shared_preload_libraries = 'pg_eddy'

Then restart PostgreSQL:

pg_ctl restart

Create the Extension

CREATE EXTENSION pg_eddy;
SELECT pg_eddy.health_check();  -- Should return 'pg_eddy OK'

Quick Start

Create Nodes

SELECT pg_eddy.create_node(
    ARRAY['Person'],
    '{"name": "Alice", "age": 30}'::jsonb
);  -- Returns node_id

SELECT pg_eddy.create_node(
    ARRAY['Person'],
    '{"name": "Bob", "age": 28}'::jsonb
);

Read Nodes

SELECT pg_eddy.get_node(1);
-- Returns: {"name": "Alice", "age": 30}

Count Nodes

SELECT pg_eddy.node_count();
-- Returns: 2

Architecture

┌─────────────────────────────────────────────────────────┐
│                     Client Layer                        │
│  pgEddy.cypher(query, params)  │  SQL / SPI interface   │
└───────────────────┬─────────────────────────────────────┘
                    │
┌───────────────────▼─────────────────────────────────────┐
│              OpenCypher Query Engine                     │
│  Lexer → Parser → AST → Logical Plan → Physical Plan    │
│  Pattern rewriting · Filter pushdown · Index selection  │
└───────────────────┬─────────────────────────────────────┘
                    │
┌───────────────────▼─────────────────────────────────────┐
│               Native Graph Storage AM                   │
│  Node Store (custom pages) │ Edge Store (CSR pages)     │
│  Property Store (inline + overflow)                     │
│  Label Index (B-tree)  │  Rel-type Index (B-tree)       │
│  Property Index (B-tree per indexed property)           │
└───────────────────┬─────────────────────────────────────┘
                    │
┌───────────────────▼─────────────────────────────────────┐
│              Reactivity Layer (optional — pg_trickle)   │
│  Graph stream tables: MATCH views, path aggregates      │
│  IVM engine · DAG scheduler · CDC change capture        │
└─────────────────────────────────────────────────────────┘

Development

Run Tests

cd pg_eddy
cargo pgrx test pg18

Run a Development PostgreSQL Instance

cd pg_eddy
cargo pgrx run pg18

Then in psql:

CREATE EXTENSION pg_eddy;
SELECT pg_eddy.health_check();

Lint

cd pg_eddy
cargo clippy --features pg18

Tasks

See justfile for common development tasks:

just build      # Build the extension
just test       # Run tests
just lint       # Run clippy
just run        # Start a development PostgreSQL instance

Performance Expectations

pg_eddy targets 2–5× faster multi-hop MATCH patterns than Apache AGE on graphs that fit in shared_buffers. Per-hop traversal is O(degree) via adjacency-follow (no index lookups).

Honest trade-offs:

  • Neo4j on in-memory graphs: ~5–10× faster (native memory access vs PostgreSQL buffer manager)
  • For I/O-bound graphs: both systems are I/O-dominated; the gap narrows to 2–3×
  • See plans/implementation_plan.md for benchmarking strategy

Documentation

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines, and plans/implementation_plan.md for the current phase and next priorities.

Compatibility

Component Version Status
PostgreSQL 18.x Required
pgrx 0.18 Required
Rust Edition 2024 Required
Linux Debian 11+, Ubuntu 20.04+ Tested
macOS 12+ (Intel/ARM) Expected to work
Windows WSL2 Expected to work

License

pg_eddy is licensed under the Apache License 2.0. See LICENSE for details.

Support

Acknowledgments

pg_eddy's design draws inspiration from:

  • Neo4j's native store: adjacency-aware layout and singly-linked relationship chains
  • PostgreSQL's AM API: MVCC, WAL, buffer management, index integration
  • OpenCypher spec: query language and TCK conformance target
  • pg-trickle: incremental view maintenance and CDC integration patterns

Roadmap

See plans/implementation_plan.md for the detailed technical roadmap with phase gates, exit criteria, and strategic rationale.

About

A Postgres extension for labeled property graphs with index-free adjacency and built-in materialized views.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages