Write to your primary OLTP rowstore table and route slow analytical aggregations to a sub-second USING mooncake columnstore mirror.
This is the multi-page printable view of this section. .
Use Cases & Architecture Patterns
- 1: Real-Time Operational Analytics (HTAP)
- 2: Archiving Large Postgres Tables
- 3: The Postgres Lakehouse
- 4: Log Analytics with OpenTelemetry
pg_mooncake enables four high-value analytical patterns directly inside PostgreSQL without introducing Kafka, Debezium, or an external data warehouse.
Mirror a time-partitioned PostgreSQL table into object storage, drop old partitions from the rowstore to save disk space, and retain full queryable history in the columnstore.
Automatically export PostgreSQL tables as Apache Iceberg v2 tables on S3 or Cloudflare R2, and query external Iceberg/Delta Lake datasets inside Postgres.
Ingest high-volume application logs and traces into PostgreSQL and run fast vectorized analytical queries over object-storage-backed Parquet files.
1 - Real-Time Operational Analytics (HTAP)
If you are running slow analytical queries (dashboards, customer-facing metrics, multi-table aggregations) on PostgreSQL tables, pg_mooncake gives you a straightforward route to accelerate them:
- Keep your application write path 100% unchanged.
- Create a real-time columnstore mirror with
CALL mooncake.create_table(dst, src). - Route analytical
SELECTqueries to the columnstore mirror table.
Key Advantages
- Schema changes supported:
ALTER TABLE ... ADD COLUMNandDROP COLUMNon the source table automatically sync to the columnstore mirror. - Fast
UPDATEandDELETEhandling:moonlinkuses in-memory Apache Arrow buffers and Roaring Bitmap deletion vectors, handling high-frequency OLTP updates and deletes far more efficiently than traditional Parquet/Iceberg writers. - Zero-ETL: No Debezium, Kafka, or Flink clusters to operate—
pg_mooncakeuses PostgreSQL’s built-in logical replication. - Transactional consistency: Analytical queries wait for the current PostgreSQL WAL LSN barrier before scanning, guaranteeing a consistent view of committed data.
Implementation Pattern: Write to Rowstore, Read from Columnstore
Manual Query Routing
pg_mooncake does not automatically rewrite queries targeting orders to target orders_analytics. You choose which table to query based on your workload:
This explicit separation gives you predictable performance: transactional operations stay fast on the B-tree-indexed heap table, while analytical scans leverage DuckDB 2.0’s vectorized execution over columnar storage.
2 - Archiving Large Postgres Tables
pg_mooncake’s columnstore mirror writes data as compressed Parquet files (on local disk or S3 / Cloudflare R2 object storage), providing cost-effective, durable history while PostgreSQL heap storage only maintains hot recent partitions.
Architecture Pattern
The partition archiving pattern follows three rules:
- Create a columnstore mirror (
CALL mooncake.create_table) of a time- or range-partitioned PostgreSQL table. - Periodically
DROPorTRUNCATEold partitions from the PostgreSQL rowstore once they age out of your hot operational window. - Retain complete history in the columnstore mirror—
pg_mooncakeintentionally ignoresDROP TABLEandTRUNCATEevents on child partitions of a partitioned source table, so all historical rows remain queryable in the columnstore!
Step-by-Step Walkthrough
Below is a complete example using a range-partitioned transactions table.
Step 1: Create a Partitioned Rowstore Table
Step 2: Create a Columnstore Mirror
Step 3: Add First Partition & Insert Data
Step 4: Drop the Old Partition from Rowstore
When transactions_p1 is cold and you want to reclaim PostgreSQL disk space immediately:
Step 5: Query Historical Data in the Columnstore
Even though transactions_p1 was dropped from the rowstore, all three rows remain intact in transactions_archive:
Step 6: Continue with New Partitions
Best Practices
- Never re-insert overlapping keys into dropped partition ranges: After dropping or truncating a rowstore partition, do not recreate a partition covering the same key range and insert conflicting rows, as the historical rows from the dropped partition are still retained in the columnstore.
- Always use declarative partitioned tables for archiving:
pg_mooncakesafely ignores partition drops and truncates on partitioned tables. - Pair with S3 / R2 object storage: Set
mooncake.default_bucket = 's3://...'before creating the mirror so archived partitions are stored directly in low-cost cloud object storage. See Iceberg & Object Storage.
3 - The Postgres Lakehouse
The Lakehouse architecture stores data once in open table formats (Apache Iceberg or Delta Lake) on object storage so multiple compute engines—PostgreSQL, DuckDB, Apache Spark, Snowflake, Trino, and Python (Polars / PyIceberg)—can share the same dataset without copying it.
pg_mooncake makes PostgreSQL a first-class citizen of your lakehouse in both directions:
- Postgres $\rightarrow$ Iceberg Lakehouse (Zero-ETL Export): Replicate operational PostgreSQL tables directly into Apache Iceberg v2 tables on S3, Cloudflare R2, or local storage via
moonlink. - Lakehouse $\rightarrow$ Postgres (Direct In-DB Scan): Query external Parquet, Apache Iceberg, and Delta Lake tables directly from PostgreSQL and join them with live operational tables via
pg_duckdb.
1. Export Operational Postgres Tables as Apache Iceberg v2
Traditionally, landing PostgreSQL data in Apache Iceberg required deploying Debezium, Kafka, Schema Registry, and Spark/Flink consumers. With pg_mooncake, it takes three SQL commands:
Inspect the generated warehouse path with SELECT * FROM mooncake.list_tables(); and point Spark, Snowflake, Trino, or PyIceberg directly at <iceberg_warehouse_location>/metadata/v*.metadata.json.
2. Query External Lakehouse Tables Inside PostgreSQL
At the same time, your PostgreSQL applications can query tables written by Spark or Databricks in S3 and join them with live PostgreSQL heap tables:
4 - Log Analytics with OpenTelemetry
PostgreSQL is rarely used for large-scale log analytics because uncompressed heap tables and indexes grow expensive quickly, and full-table scans over millions of log entries are slow.
With pg_mooncake, you can combine partitioned PostgreSQL log ingestion with an object-storage-backed columnstore mirror:
- Low storage cost: Historical logs are compressed into columnar Parquet files on S3, Cloudflare R2, or local disk.
- Simple self-hosting: Just PostgreSQL + object storage—no Elasticsearch or ClickHouse cluster required.
- Fast vectorized scans: DuckDB 2.0 scans millions of log records per second with segment elimination on timestamps.
Implementation Pattern
Combine a time-partitioned rowstore table for log ingestion with a USING mooncake columnstore mirror for analytical queries:
As daily log partitions age out (for example, after 24 or 48 hours), drop the old partitions from application_logs (DROP TABLE application_logs_2026_11_01;) as described in Archiving Large Postgres Tables. The complete log history remains queryable in application_logs_analytics.