Architecture
pg_mooncake (v0.3.0) combines three specialized components inside a single PostgreSQL deployment:
pg_duckdb(v1.2.0): Embeds the DuckDBv2.0.0vectorized analytical execution engine inside PostgreSQL and routes queries against non-heap table access methods.moonlink(v0.0.1): A Rust library and background worker embedded insidepg_mooncakethat ingests PostgreSQL logical replication streams, maintains an in-memory Apache Arrow write buffer and Roaring Bitmap deletion vectors, flushes Parquet files, and emits Apache Iceberg v2 table metadata.duckdb_mooncake(v0.0.2): A DuckDB 2.0 C++/Rust storage extension loaded automatically bypg_mooncakefrom a local extension repository (mooncake.extension_repository). It connects to Moonlink over a local Unix domain socket RPC (pg_mooncake/moonlink.sock) to read consistent snapshots across both flushed Parquet files and buffered Arrow batches.
Together, they expose a native PostgreSQL Table Access Method (USING mooncake) managed through CALL mooncake.create_table(dst, src).

Component Pinning in pg_mooncake 0.3.0
| Component | Version / Tag | Pinned Commit (versions.json) |
Role |
|---|---|---|---|
pg_mooncake |
v0.3.0 |
1a9cbfd65be9a6486d020120acb49ef0c6800d68 |
PostgreSQL extension (pgrx 0.19.3), SQL procedures, USING mooncake TAM, and background worker launcher |
moonlink |
v0.0.1 |
b298ef7444890e3c8af5544e350232518bcfd8e5 |
Embedded Rust CDC ingestion engine, Arrow buffer, deletion vectors, and Iceberg v2 snapshot writer |
duckdb_mooncake |
v0.0.2 |
49765a369ad88986916ce53c969ed2ce4ba66fac |
DuckDB 2.0 extension (mooncake.duckdb_extension) scanning Moonlink tables and Parquet statistics |
pg_duckdb |
v1.2.0 |
8756099e267c47f0b86c24de4cf995327f1f4279 |
Embedded DuckDB execution bridge inside PostgreSQL (enforced by SQL installation guard) |
duckdb |
v2.0.0 |
d54cbbe3df17e8e5ce68312f483b4155836112aa |
Underlying C++ vectorized analytical query engine |
Write Path
In pg_mooncake, you never write directly to the USING mooncake columnstore table. Instead, you write to your regular PostgreSQL rowstore (heap or partitioned) table:
- Initial Table Copy & Slot Creation: When you execute
CALL mooncake.create_table('dst', 'src'),pg_mooncake:- Inspects the source table’s schema and creates the destination table with
USING mooncake. - Sets
REPLICA IDENTITY FULLon the source table (and, inv0.3.0, automatically on all existing leaf partitions if the source is a partitioned table) if the source table does not have a primary key. - Creates a PostgreSQL publication and logical replication slot (
pgoutput), copies existing rows fromsrcinto Moonlink, and records the starting WAL LSN.
- Inspects the source table’s schema and creates the destination table with
- Continuous Logical Replication: A background worker (
mooncake_bgworker) continuously consumes WAL changes (INSERT,UPDATE,DELETE) from the logical replication slot and streams them tomoonlink. - Buffered Arrow + Deletion Vectors:
- Inserts are appended to an in-memory Apache Arrow write buffer (
write_buffer_size, default32 MB), making new rows immediately visible to analytical queries without writing a tiny Parquet file per transaction. - Deletes record the target row’s location in an in-memory/on-disk Roaring Bitmap deletion vector, avoiding expensive full-file Parquet rewrites on every
DELETE. - Updates are processed atomically as a deletion vector entry for the old tuple plus an append to the Arrow buffer for the new tuple.
- Inserts are appended to an in-memory Apache Arrow write buffer (
- Background Compaction & Parquet Flush: Once the write buffer reaches its threshold or when
CALL mooncake.create_snapshot('dst')/CALL mooncake.optimize_table('dst', 'full')is invoked, Moonlink flushes the buffered Arrow records into compressed Parquet files and updates the Apache Iceberg v2 metadata.
Read Path
When you run a SELECT query that references a USING mooncake columnstore table:
- Query Routing via
pg_duckdb: PostgreSQL’s planner recognizes themooncaketable access method (registered withpg_duckdbviaRegisterDuckdbTableAm("mooncake")) and hands the query plan over to the embedded DuckDB 2.0 engine. - Automatic Extension Attachment: On the first scan in a backend session,
pg_mooncakeconfiguresSET extension_directory = '<mooncake.extension_repository>', loads themooncakeDuckDB extension (duckdb_mooncake v0.0.2), and executesATTACH '' AS mooncake (TYPE mooncake, METADATA '<pgdata>/pg_mooncake'). - LSN Synchronization Barrier: Before scanning a table,
duckdb_mooncakequeries PostgreSQL’s current WAL write/insert LSN and instructs Moonlink via RPC (scan_table_begin) to wait until the replication stream has ingested up to that LSN—guaranteeing read-your-own-writes transactional consistency. - Hybrid Arrow + Parquet Scan with Segment Elimination:
- Moonlink returns the list of active Parquet data files, active deletion vectors, and a pointer to the current in-memory Arrow buffer.
duckdb_mooncakereads Parquet column min/max statistics through DuckDB 2.0’s exported multi-file reader interface to skip irrelevant files and row groups (segment elimination), applies deletion vectors, and streams the combined results back to PostgreSQL.
Storage Layout
By default (mooncake.default_bucket = ''), Moonlink stores table metadata, deletion vectors, and Parquet data files under your PostgreSQL data directory at $PGDATA/pg_mooncake/.
When mooncake.default_bucket is set to an s3:// or r2:// URI (with credentials configured in duckdb.secrets), Moonlink writes Parquet data files and Apache Iceberg v2 metadata (metadata/*.metadata.json and Avro manifest lists) directly to object storage. You can inspect the active warehouse path and LSN progress for every mirrored table at any time with: