Create your first USING mooncake columnstore mirror table, run transactional DML, and execute vectorized joins.
This is the multi-page printable view of this section. .
Get Started & Installation
This guide covers installing pg_mooncake (v0.3.0) (which bundles moonlink (v0.0.1) and duckdb_mooncake (v0.0.2)) alongside pg_duckdb (v1.2.0) on PostgreSQL 14, 15, 16, 17, and 18.
1. Install Binary Packages via Pigsty (Recommended)
The fastest way to install pg_mooncake on Linux (el8, el9, el10, d12, d13, u22, u24, u26 across x86_64 and aarch64) is with the pig package manager maintained by PGSTY. See the Download & Availability Matrix for direct package links.
2. Configure postgresql.conf & Enable Extension
pg_mooncake uses PostgreSQL logical replication to stream rowstore changes into Moonlink and uses pg_duckdb to load the bundled mooncake.duckdb_extension binary. Add the following settings to postgresql.conf (or via ALTER SYSTEM):
Restart PostgreSQL (sudo systemctl restart postgresql-18 or pig pg restart), then connect with psql and create the extension:
pg_duckdb 1.2.0 Version GuardDuring CREATE EXTENSION pg_mooncake, the bootstrap SQL script explicitly verifies that pg_duckdb version 1.2.0 is installed in pg_extension. Attempting to install pg_mooncake 0.3.0 against an incompatible pg_duckdb release (such as 1.1.x or 1.3.x) will fail immediately with a clear error message. Always upgrade pg_duckdb and pg_mooncake together.
3. Build from Source (v0.3.0)
To build pg_mooncake 0.3.0, pg_duckdb 1.2.0, duckdb_mooncake v0.0.2, and moonlink v0.0.1 from source:
Prerequisites
- PostgreSQL development packages:
postgresql-server-dev-14through18(orpostgresql17-devel/postgresql18-devel) - C/C++ build toolchain:
cmake,ninja-build,g++orclang,libssl-dev,libcurl4-openssl-dev,liblz4-dev,libzstd-dev,protobuf-compiler(protoc) - Rust toolchain: Rust
1.99.0+(stable) andcargo-pgrx0.19.3:
Clone, Verify Pins & Compile
4. Docker Image (Obsolete)
Obsolete — pending updates and maintenance. PGSTY has not yet published Docker or Kubernetes container images for pg_mooncake 0.3.0. The upstream command below uses the historical mooncakelabs/pg_mooncake image (PostgreSQL 17 with pre-0.3 pg_mooncake) and is retained for reference only.
Next Steps
Review operational requirements around primary keys, partitioned tables, TRUNCATE, and supported DDL operations.
1 - Hello World Tutorial
This tutorial walks through the core operations of pg_mooncake: creating a regular PostgreSQL rowstore table, creating a real-time columnstore mirror (USING mooncake), running transactional INSERT, UPDATE, and DELETE statements, testing rollback semantics, and joining columnstore and rowstore tables.
1. Enable the Extension
Connect to your PostgreSQL database and ensure pg_mooncake (along with pg_duckdb) is enabled:
2. Create a Rowstore Table & Columnstore Mirror
First, create a standard PostgreSQL heap table (user_activity) for your transactional writes, then call mooncake.create_table to create its real-time columnstore mirror (user_activity_columnstore):
Behind the scenes, mooncake.create_table creates user_activity_columnstore USING mooncake, sets up a logical replication slot on user_activity, and registers the table with moonlink.
3. Insert, Update, and Delete Rows
Perform all writes (INSERT, UPDATE, DELETE) on the primary rowstore table (user_activity), and run your analytical SELECT queries on the columnstore mirror (user_activity_columnstore):
Output:
4. Transactional Consistency & Rollback
Moonlink only ingests committed PostgreSQL transactions from the logical replication stream. Uncommitted or rolled-back transactions never appear in the columnstore mirror:
5. Joins Across Columnstore & Rowstore Tables
You can join two columnstore tables together, or join a USING mooncake columnstore table directly with a regular PostgreSQL heap table in the same SQL query:
6. Inspect Replication Status & Create an Iceberg Snapshot
Check the current commit LSN, flushed LSN, and Iceberg warehouse directory using mooncake.list_tables(), or force-flush buffered Arrow data into Parquet + Apache Iceberg v2 metadata with mooncake.create_snapshot:
2 - Known Limitations
Before deploying pg_mooncake (v0.3.0) in production, review the following architectural constraints and known limitations.
1. Read-Only Columnstore Mirror Tables
Tables created with CALL mooncake.create_table(dst, src) use the USING mooncake table access method and are read-only replicas.
- Direct
INSERT,UPDATE,DELETE, orCOPYstatements targeting theUSING mooncaketable are rejected by PostgreSQL (ERROR:cannot modify table "..." because it uses the mooncake access method). - Always execute DML operations against the source rowstore table (
src); changes replicate automatically viamoonlink.
2. Primary Keys & REPLICA IDENTITY FULL
To replicate UPDATE and DELETE operations accurately over PostgreSQL logical replication, the source table must provide complete old-row identification in the WAL stream:
- Tables with a Primary Key: Work out of the box using the primary key.
- Tables without a Primary Key:
CALL mooncake.create_table(dst, src)automatically executesALTER TABLE src REPLICA IDENTITY FULLbefore starting replication. - Partitioned Tables (
v0.3.0behavior):- In
pg_mooncake 0.3.0, callingmooncake.create_tableon a partitioned table without a primary key automatically setsREPLICA IDENTITY FULLon both the partitioned root table and all existing leaf partitions (fixing an earlier bug where partitionUPDATEevents lacked complete old tuples and caused a replication worker panic). - Important: If you attach or create new partitions after
mooncake.create_tablehas already run, you must explicitly runALTER TABLE <new_partition> REPLICA IDENTITY FULL;on that new partition before executingUPDATEorDELETEstatements on it.
- In
3. TRUNCATE Behavior
- Regular Heap Tables:
TRUNCATEon a mirrored regular heap table is not replicated to the columnstore mirror. - Partitioned Tables (Archiving Pattern):
TRUNCATEorDROP TABLEon individual old partitions of a partitioned source table is intentionally ignored by the columnstore mirror so you can drop cold partitions from PostgreSQL heap storage while retaining the complete historical dataset in the columnstore. See Archiving Large Postgres Tables.
4. Schema Evolution (ALTER TABLE)
Only the following ALTER TABLE operations on the source rowstore table are automatically replicated to the columnstore mirror:
ALTER TABLE <src> ADD COLUMN <col> <type>ALTER TABLE <src> DROP COLUMN <col>
Other DDL modifications (such as ALTER COLUMN ... TYPE or RENAME COLUMN) are not automatically propagated; drop and recreate the columnstore mirror (DROP TABLE <dst>; CALL mooncake.create_table('<dst>', '<src>');) when performing complex schema migrations.
5. Unsupported Data Types
While pg_mooncake supports all common PostgreSQL numeric, text, boolean, date/time, UUID, JSON/JSONB, and array types, the following types are currently unsupported or have caveats:
- PostgreSQL custom
ENUMtypes and composite/domain types - Geometric and network address types not mapped by
pg_duckdb - Very high-precision
NUMERICvalues exceeding DuckDB’sDECIMAL(38, s)width
6. mooncake.load_files (SQLSTATE 0A000)
In pg_mooncake 0.3.0, calling CALL mooncake.load_files(dst, files) explicitly raises SQLSTATE 0A000 (ERRCODE_FEATURE_NOT_SUPPORTED: "mooncake.load_files is not implemented by the bundled Moonlink version").
- Previously, the bundled Moonlink server’s
import_datahandler was a no-op stub that returned success without ingesting any rows. - To load external Parquet or CSV files, either load them into the source PostgreSQL table with
COPY/INSERT INTO ... SELECT * FROM duckdb.read_parquet(...), or query them directly in place as described in Query Your Data Lake in Postgres.