Repository navigation
0.5.0 – 2026-10-17
Release Notes
Added
- Dedup on read. In tables with dedup on, the Timelake reader (CLI, Python, Rust) returns only the latest write of
each(series, time), before compaction has removed the older copies. Only files that may share a key are merged;
the rest are read as before. DuckDB, other Iceberg readers and the TypeScript client still see the copies until
compaction. It can be turned off per table (timelake.read-dedup = false) or per session
(SET timelake.read_dedup = false, or--set timelake.read_dedup=falseon the CLI). A second strategy,
timelake.read-dedup-strategy = keys-first, lists the older copies first and leaves them out of a normal scan. - Cold partitions compact sooner. Once newer data is
timelake.compact-cold-after(default 1 hour) past a
partition's end, inline maintenance rewrites the partition into sorted, duplicate-free files when it has two small
files, even if the append didn't touch it. - Insertion time. Time-series tables get an
_ingest_tscolumn: each append fills it with the time the append
started, unless the input brings its own values (an import keeping arrival times). Among rows with the same
(series, time)the latest insertion wins, then the later commit, everywhere: within an append, on read, in
compaction and in the last-value index. DuckDB can pick the same rows with
QUALIFY row_number() OVER (PARTITION BY <series>, <time> ORDER BY _ingest_ts DESC) = 1. Leave it out with
ingest_time=False(Python) or--no-ingest-time(CLI);timelake infoshows it. - Settings. Every switch is declared in one registry, with its default, the levels it can be set at (creation,
table, session, call) and the version that added it.timelake settingslists them, and with a table name shows
the table's values;timelake alter <table> --set read-dedup=false --unset retentionchanges a table's settings
(Table.set_propertiesin Python and Rust).Lake(..., settings={...})in Python sets session settings for every
query, andSHOW timelake.read_dedupshows a session's value. Names work with dashes or underscores, with or
withouttimelake.. The docs page "Table properties" is now "Settings". - Rollups.
CREATE MATERIALIZED ROLLUP iot.readings_1h AS SELECT device, time_bucket(INTERVAL '1 hour', time) AS bucket, avg(value), max(value), count(*) FROM iot.readings GROUP BY device, bucketcreates a table of aggregates
per time bucket and computes it. Maintenance keeps it up to date: inline after appends once its last refresh is
older thantimelake.rollup-refresh-interval(default 1 minute), and on everytimelake maintain;
REFRESH MATERIALIZED ROLLUPrefreshes it now andDROP MATERIALIZED ROLLUPremoves it. AGROUP BY time_bucket(…)
query on the raw table with the same width and grouping reads the buckets the rollup has right from it, and the rest
(buckets changed since the refresh, partial buckets at the edges of the time range) from the raw table, so the
answer is the same as without it;SET timelake.rollup_routing = falseturns that off. First version: one level from
the raw table, grouped by series-key columns, withcount,sum,min,max,avg,first_valueand
last_value.timelake maintainandTable.maintain()reportrollups_refreshedandrollup_rows
(--no-rollups,rollups=Falseto skip). The docs have a new page, "Rollups". - Block cache. Reads of files a table never changes (data files, manifests, manifest lists, last-value index
files) go through a cache of 1 MiB blocks, in memory by default on object stores and HTTPS (256 MiB), and on local
disk withtimelake_cache=disk, shared by every process on the machine (4 GiB). A query still checks for a newer
table version, so answers are as fresh as before. Storage optionstimelake_cache,timelake_cache_dir,
timelake_cache_memory_bytes,timelake_cache_disk_bytesandtimelake_cache_block_bytes;Lake.cache_stats()
in Python andStorage::cache_stats()in Rust count hits and misses. On S3, TSBS IoT repeat queries went from
54 to 16 ms with no requests, and 16 clients from 288 to 398 queries per second.
Changed
- Table format 0.3.0 for new tables (the insertion-time column). Timelake 0.4 and older can't open them: upgrade
every reader and writer first.timelake upgraderaises older tables to 0.3.0 but doesn't add_ingest_ts(a
table's columns are fixed at creation for now). - Settings are checked. Creating or altering a table with an unknown
timelake.*property, a bad value, or a
setting fixed at creation (time-column,series-key,chunk-interval,dedup,ingest-time-column) as a
property is an error; use the table options for those.timelake.format-versioncan no longer be set by hand.
SETof a table-only setting in SQL is an error.timelake sql --setwith a bad key reports an error instead of
crashing. - Rust:
SessionSettingshasgetandflaginstead of public fields.Session::parsereturns
timelake::Statement(DataFusion's statement, or a rollup statement) forSession::execute.
MaintainOptionshas arollupsfield andMaintainReporthasrollups_refreshedandrollup_rows. - With dedup on, rows with the same
(series, time)within one append keep only the last, so the append'srows
counts them once. Files written by earlier versions may still hold such copies until compaction. - Benchmarks: Timelake's repeat runs on object storage now read from its memory cache, so their times drop from
this version on; first runs, in a fresh process, are unaffected.timebench run --reusealso runs the concurrent
dashboards, andTIMEBENCH_TIMELAKE_OPTIONSpasses storage options to Timelake's readers. - The TypeScript client's WebAssembly reader is about 4% smaller (6.7 MB), built with Rust 1.99.
Fixed
- Compaction could let an older write win. Inline compaction merged files of a similar size even when a file of
another size was committed between them, and the merged file then counted as newer than that file: where both held
the same(series, time), the older value won from then on. Compaction now merges only files next to each other in
commit order, and a merged file keeps the data sequence number of the newest file it replaces.
Install timelake-cli 0.5.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/polythings/timelake/releases/download/v0.5.0/timelake-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/polythings/timelake/releases/download/v0.5.0/timelake-cli-installer.ps1 | iex"Download timelake-cli 0.5.0
| File | Platform | Checksum |
|---|---|---|
| timelake-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| timelake-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| timelake-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| timelake-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| timelake-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo polythings/timelakeYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation> --repo polythings/timelake