---
title: Performance
description: Transactional throughput and latency against Postgres, analytical results on ClickBench, and the machine and methodology behind every number.
---

In the 2026-08-31 benchmark campaign, Postgres had 1.8–2.6× the point-read throughput and 1.2–3.6× the durable single-row update throughput of Lakefront's HTTP node. Lakefront's hot ClickBench total was approximately 0.5% higher than native DuckDB.

The tables below report throughput and latency for transactional workloads against Postgres, and analytical query times against native DuckDB.

## Machine and setup

Every number on this page comes from one campaign, run 2026-08-31:

|           |                                                                      |
| --------- | -------------------------------------------------------------------- |
| CPU       | Apple M4 Pro                                                         |
| Memory    | 48 GB                                                                |
| OS        | macOS 26.6.2                                                         |
| Postgres  | 17, in Docker (OrbStack), reached over the host port map             |
| Lakefront | this repo at the commit publishing these numbers, `writePath: "wal"` |

Both engines are reached through a Docker port mapping from the host running the client. Running pgbench inside the container instead uses a loopback connection; in this campaign, that increased Postgres select-only throughput by 7–19×. The harness warns when it uses that configuration.

Lakefront acknowledges writes after a group-committed, fsynced Postgres commit. The comparison uses Postgres with `synchronous_commit=on` to match that durability requirement. The `sync=off` row is included as a reference with weaker durability.

## Transactional performance

pgbench's own builtin workloads at scale 1 (100,000 rows). Latencies are per-operation.

Compare Postgres over TCP with Lakefront's HTTP node: both include a network hop. Embedded Lakefront results measure in-process execution and exclude the HTTP and network overhead.

Multi-statement transactions had approximately half Postgres's throughput, even with Lakefront measured embedded.

### Point reads

pgbench select-only, against Postgres over TCP and a Lakefront server node over HTTP. The replica is warm, ART-indexed on `id`.

| Clients | System         | tps    | p50      | p95      |
| ------- | -------------- | ------ | -------- | -------- |
| 1       | Postgres       | 14,006 | 0.071 ms |          |
| 1       | Lakefront HTTP | 6,479  | 0.139 ms | 0.219 ms |
| 8       | Postgres       | 44,855 | 0.178 ms |          |
| 8       | Lakefront HTTP | 25,023 | 0.297 ms | 0.435 ms |
| 32      | Postgres       | 72,439 | 0.442 ms |          |
| 32      | Lakefront HTTP | 28,116 | 1.045 ms | 1.667 ms |

Postgres had 1.8–2.6× the point-read throughput of a Lakefront HTTP node. Lakefront's p50 latency was 0.297 ms at 8 clients and 1.045 ms at 32. Its request path includes HTTP and JSON handling.

Embedded reads measured 0.079 ms p50 at 1 client and 0.412 ms at 32 (11,783 and 71,565 tps). These results show the cost of querying a local DuckDB replica without the HTTP serving layer.

### Single-row updates

A bare update, no transaction wrapper on either side.

| Clients | System                                | tps    | p50      | p95       |
| ------- | ------------------------------------- | ------ | -------- | --------- |
| 1       | Postgres sync=on                      | 1,282  | 0.780 ms |           |
| 1       | Lakefront HTTP                        | 357    | 1.432 ms | 5.981 ms  |
| 8       | Postgres sync=on                      | 2,035  | 3.931 ms |           |
| 8       | Lakefront HTTP                        | 1,673  | 2.542 ms | 10.189 ms |
| 8       | Postgres sync=off (weaker durability) | 43,747 | 0.183 ms |           |

At one client, Postgres had 3.6× Lakefront's update throughput. Each Lakefront write includes request handling and a WAL append committed to Postgres before acknowledgment. Flushing records into the lake happens later, outside the request path.

At eight clients the throughput gap narrowed to 1.2×. Group commit combines concurrent writes into shared Postgres transactions, amortizing the fsync cost.

### Where a write spends its time

For one durable write, in order:

1. HTTP request parsing and contract validation on the node.
2. A per-board advisory lock and the WAL append, as a single plpgsql call: one round trip, one fsynced Postgres commit.
3. The ack returns. The write is durable at this point.
4. Later, off the request path: the flusher drains WAL records into Parquet, and replicas sync so reads observe the change.

The caller waits for the Postgres commit. Lake flushing and replica synchronization proceed independently afterward.

## Analytical performance

The full ClickBench suite: one 99,997,497-row web-analytics table, 43 scan/aggregation queries, run three times each. "Hot" is the best repeat run; "cold" is the first touch.

| System                                  | Queries | Hot total | Hot p50 | Hot p95 | Cold total |
| --------------------------------------- | ------- | --------- | ------- | ------- | ---------- |
| Lakefront (board over a warm replica)   | 43/43   | 10.22 s   | 118 ms  | 713 ms  | 15.31 s    |
| DuckDB native (same machine, same data) | 43/43   | 10.17 s   | 154 ms  | 617 ms  | 14.44 s    |

Lakefront's hot total was 10.22 s versus 10.17 s for native DuckDB, a difference of approximately 0.5%. Queries execute directly in the warm DuckDB replica.

## Re-measuring

These results come from one campaign on one machine. Network path, disk, and tuning affect the measurements. The [benchmarking guide](/guides/benchmark) covers running the harnesses, including host-side pgbench and both ClickBench variants.
