DuckLake stores table data as Parquet files in object
storage and hosts its catalog in a SQL database: SQLite, Postgres, or DuckDB
(served over Quack). The database layer provides
transactional consistency and ACID guarantees, but requires hosting an
additional server.
Moraine removes the need for a separate catalog database. Instead, it serves the
catalog from SlateDB, an embedded transactional key-value
store that also lives in object storage. That means that the catalog resides in
the same bucket as the data files. In addition, Moraine is the only DuckLake
extension that offers DuckDB native indexes for faster querying.
LOAD 'moraine.v2.0.0.linux_amd64.duckdb_extension';
CREATE SECRET aws_secret (TYPE s3, KEY_ID '…', SECRET '…', REGION 'us-west-2');
ATTACH 'ducklake:moraine:s3://bucket/lake' AS lake
(DATA_PATH 's3://bucket/lake-data/', READ_WRITE);
CREATE TABLE lake.events (id BIGINT, payload VARCHAR);
INSERT INTO lake.events VALUES (1, 'hello, bucket'), (2, 'hello again');
SELECT * FROM lake.events;
CREATE UNIQUE INDEX events_by_id ON lake.events (id);
SELECT payload FROM lake.events WHERE id = 2;
- No catalog server: No catalog server to deploy or monitor. The only
permanent resource is a storage bucket.
- Nothing to pay for while idle: An idle lake costs only storage, no
compute or database hosting fees.
- Bucket durability: The catalog is objects in the bucket, so it inherits
the bucket’s durability, replication, and versioning. A copy taken while no
writer is attached is a complete lake; see
Backups and copies.
- Indexed lookups: Ordered indexes over table columns, created with
CREATE INDEX and maintained in the same commit as the rows, so a query
by key probes the index instead of scanning every file; see
Index a column.
- Sub-5 ms commits. A commit is durable once one write-ahead-log PUT
lands: 50 to 100 ms on S3 Standard, 5 to 10 ms with the log on S3 Express
One Zone via
META_WAL_PATH.
- Many independent writer processes. One process writes; the rest
attach
READ_ONLY. See
one writer, many readers.
INSTALL moraine FROM community. The listing is in progress. Until
then, signed builds come from Moraine’s own extension repository, which
takes one CREATE EXTENSION REPOSITORY to register.
Identical DuckLake workloads through the same DuckDB CLI, only the catalog
swapped. Local disk, loopback Postgres 16, medians of three runs on an
8-vCPU x86-64 VM:
|
Moraine |
DuckDB file |
Postgres |
| attach |
8 ms |
19 ms |
113 ms |
| create table and insert 100,000 rows |
94 ms |
102 ms |
124 ms |
| 20 single-row commits, one connection |
254 ms |
218 ms |
135 ms |
| 128 single-row commits over 8 connections |
417 ms |
1.76 s |
1.77 s |
| 128 single-row commits over 16 connections |
461 ms |
2.85 s |
2.94 s |
Serial commits pay a write-ahead-log flush each, where a local Postgres is
faster. Overlapping commits can share one flush, giving Moraine more competitive
performance. On S3, each flush is one PUT round trip, about 30 ms same-region.
For more details, see
Benchmarks.
Moraine is under active development and in production use at
contour.app. All DuckLake SQL, including CREATE,
INSERT, UPDATE, DELETE, time travel, and maintenance, runs against
Moraine.
Dual-licensed under
MIT or
Apache-2.0.
More details
- Getting started: how to install the extension and
attach a lake.
- Configuration: attach options, environment variables,
and DuckDB functions.
- Operating manual: retention, maintenance,
encryption, backups, and upgrades.
- Comparison: Moraine alongside
DuckDB, Postgres, Quack, and Iceberg-style catalogs.
- Design: key design decisions and rationale.
- Benchmarks: performance benchmarks against other DuckLake
catalogs.
- Releases: changelog, extension downloads, and the
DuckDB compatibility table.
- Roadmap: what’s next for Moraine.