Plant data as it really arrives, 30 days of it, queried like plant dashboards and reports, against databases and lakes.
Our own benchmark: the data generator, the queries, and the method are all timebench's.
Answers are checked against a copy of the data with each row once. Each section shows the newest run on the storage you pick.
After each system's maintenance, first run of each query in a fresh process. Systems maintain at different times: Timelake while it ingests, most others in a separate step after it, some not at all; the panel below says which, and how long the step after ingest took.
TunedRun of 2026-10-20
When each system maintains its tables, and how long its step after the last commit took.
The geometric mean over the 10 of 11 queries every reader answered. First run of every reader; lower is better.
First-run time in each of the 1 run on this storage; dashed: a system read by another engine.
One run so far: the history starts with the next.
Right after the last commit, before any maintenance: the state readers see while data streams in.
TunedRun of 2026-10-20
The geometric mean over the 10 of 11 queries every reader answered. First run of every reader; lower is better.
First-run time in each of the 1 run on this storage; dashed: a system read by another engine.
One run so far: the history starts with the next.
After a day of data has arrived.
TunedRun of 2026-10-20
The geometric mean over the 11 queries. First run of every reader; lower is better.
First-run time in each of the 1 run on this storage; dashed: a system read by another engine.
One run so far: the history starts with the next.
After seven days of data have arrived.
TunedRun of 2026-10-20
The geometric mean over the 11 queries. First run of every reader; lower is better.
First-run time in each of the 1 run on this storage; dashed: a system read by another engine.
One run so far: the history starts with the next.
How each system took the data in and kept it in shape: load rate, commit times, maintenance, and what it stored.
TunedRun of 2026-10-20
rows per second, history in commits of an hour of data; higher is better
median time per commit of the live day; lower is better
Committing is the time spent in all commits together; commit times are the live day's, a 5-minute window per commit. Storage is all files after maintenance, counting replaced files still waiting for cleanup.
| System | Committing | Commit p50 | Maintenance | Files | Storage |
|---|---|---|---|---|---|
| Timelake | 404 s | 95 ms | 1.8 s | 149 | 757 MB |
| Iceberg (PyIceberg) | 17 min | 1151 ms | 175 s | 1,069 | 2.2 GB |
| Lance | 348 s | 443 ms | 27.8 s | 261 | 3.5 GB |
| Parquet files | 232 s | 179 ms | 276 s | 31 | 1.7 GB |
Scroll the table sideways for every column.
Fresh readers during ingest: whether they saw every finished commit, and how fast dashboard queries ran under writes.
TunedRun of 2026-10-20
Every 15 s during ingest, a fresh reader ran the dashboard queries and checked the newest row it could see. Behind: probes that missed a commit that had already finished.
| System | Probes behind | Most behind | Dashboard query, median |
|---|---|---|---|
| Timelake | 0 of 17 | — | 226 ms |
| Iceberg (PyIceberg) | 0 of 5 | — | 1.07 s |
| Lance | 0 of 8 | — | 5.26 s |
| Parquet files | 0 of 6 | — | 5.63 s |
Scroll the table sideways for every column.
Several clients at once after maintenance, each with its own reader, running the dashboard queries in turn.
TunedRun of 2026-10-20
After maintenance, 1, 4, 16 clients at once, each with its own reader open, running the dashboard queries in turn for 30 s. Queries per second over all clients, and the 95th percentile latency.
| System | 1 client | 4 clients | 16 clients |
|---|---|---|---|
| Timelake | 41.4 q/sp95 40 ms | 67.6 q/sp95 116 ms | 68.5 q/sp95 451 ms |
| Iceberg (PyIceberg) | 2.5 q/sp95 816 ms | 3.5 q/sp95 2.87 s | 0.7 q/sp95 353 s9 errors |
| Lance | 0.3 q/sp95 5.64 s | 0.4 q/sp95 15.5 s | 0.3 q/sp95 1491 s8 errors |
| Parquet files | 10.6 q/sp95 139 ms | 24.0 q/sp95 262 ms | 24.9 q/sp95 1.08 s |
Scroll the table sideways for every column.
Requests and storage at the store's list prices; machines are not included.
AWS S3TunedRun of 2026-10-20
Ingest is scaled to a month of data arriving at this rate.
| System | Ingest, per month | Storage, per month | Per query |
|---|---|---|---|
| Timelake | $0.049 | $0.016 | $0.000023 |
| Iceberg (PyIceberg) | $0.098 | $0.048 | $0.000097 |
| Lance | $0.26 | $0.076 | $0.0014 |
| Parquet files | $0.058 | $0.036 | $0.000038 |
Scroll the table sideways for every column.
Left out of the rankings above; each query's list says which readers it lost.
TunedRun of 2026-10-20
Each system is written, maintained, and read the way its own docs suggest, on the same data. An entry in a ranking is a system, or a system and the reader that queried it, such as Lance + DuckDB.
Every system runs in two tunings. Tuned: each system as its docs recommend, precomputation included. Default: each system as installed. Results are tuned unless a section's switch says otherwise.
Timelake's own writer: Iceberg tables of sorted Parquet, compacted as it appends.
Plain Iceberg tables written by PyIceberg appends: what a Firehose stream into S3 Tables writes.
Lance datasets, appended commit by commit.
Parquet files without a table format: a new file per commit, as an S3 sink writes them.
TimescaleDB, a PostgreSQL database for time series, on its server's own disk.
ClickHouse MergeTree tables on its server's disk, ordered by series key and time.
QuestDB WAL tables partitioned by day, on its server's disk.
InfluxDB 3 Core with its query file limit raised, as its docs allow, so long time ranges can be queried.
InfluxDB 3 Core as shipped: a query reads at most 432 Parquet files, about 3 days of data, so longer ones fail.
Each system's own reader after maintenance: the geometric mean of first runs over the queries it completed in each run. The weekly run adds a point each week.
1 run
7 runs
Hover or tap ⓘ for the machine, the commit and every version.