Summary
Every engine feature has a setting that turns it on or off or picks its behaviour, at the table, session or call level, declared once in a registry that drives validation, SHOW, the CLI and the docs.
Design
Principle. Every engine feature has a setting that turns it on or off or picks its behaviour, a bit like Postgres's configuration variables. During fast development this keeps trade-offs explicit, lets benchmarks measure each feature on and off, and records how behaviour changed.
One registry. Every setting is declared once (in timelake-format): name, type, default, the levels it may be set at, a description, and the version that added it. From it come the docs page, SHOW timelake.* in SQL, timelake settings on the CLI, and validation: an unknown name or a bad value is an error, not ignored.
Levels, the more specific winning:
| Level | How it's set | Lifetime |
|---|---|---|
| Built-in default | the registry | — |
| Table | table property timelake.<name>, at creation or later (timelake alter, Table.set_properties) | stored in the table, applies to every reader and writer |
| Session | SQL SET timelake.<name> = …; Session / Lake options in Rust and Python; --set <name>=<value> on the CLI | one connection or process |
| Call | AppendOptions, MaintainOptions, … | one call |
A setting declares which levels it allows: the time column and series key only at creation; read settings at every level; write settings at table and call level.
First settings:
| Setting | Values (default) | Levels | Meaning |
|---|---|---|---|
read-dedup | on, off (on when dedup is on) | table, session | Remove copies when the Timelake reader reads (spec 12) |
read-dedup-strategy | merge, keys-first (decided by benchmark) | table, session | How copies are removed on read (spec 12) |
append-dedup | on, off (on when dedup is on) | table, call | Keep only the last of equal keys within one append (not built, see below) |
ingest-time-column | a column name, or none (_ingest_ts) | creation | Insertion time per row (spec 12), built |
latest-by | a column (_ingest_ts) | table | Which copy of a row is the latest write (spec 10, proposed) |
as_of_inserted | a timestamp, or none | session | Leave out rows inserted after it, before dedup (spec 10, proposed) |
compact-cold-after | a duration, or off (1h) | table, call | Full compaction of partitions whose time range has ended (spec 12) |
version-check-interval | a duration (1s) | table, session | How long the reader uses a table version without checking for a newer one (spec 7), built |
data-page-rows | a count (20,000; 8,192 when written from WebAssembly) | table | Rows per Parquet data page (spec 13), built |
The existing timelake.* properties become registry entries unchanged. Hard-coded behaviour moves into settings as it's touched: compaction tiers, the small-file ratio, scan parallelism. The planning cache's lifetime became version-check-interval.
Decided 2026-10-16:
- Names in SQL use underscores (
SET timelake.read_dedup = off); every spelling (dashes, underscores, with or withouttimelake.) names the same setting. - No locked settings: a session override is the reader's choice.
- No environment variables until something needs them.
Built 2026-10-16:
- The registry is
timelake_format::settings: name, kind, default, levels, the version that added it, description, and a value check. A test keeps it in step with the contract's properties and defaults. - Table level: properties are checked at creation and by
Table::set_properties(CLItimelake alter --set/--unset, PythonTable.set_properties). Unknowntimelake.*names, bad values and settings not allowed at the table level are errors; properties a newer Timelake wrote are ignored when reading. - Session level: every setting the registry allows there can be
SET, shown withSHOW, passed with--set, or given to Python'sLake(settings=…). The CLI's--setno longer panics on a bad key. timelake settings [table] [--json]lists them; the docs page "Settings" renders the same list (dashboard/lib/settings.json, checked by a test).- Unreleased: a setting's kind is a type (
settings::Kind) that names its values for the docs and checks them; a setting with a fixed set of values is an enum (timelake_format::value) that the contract, the registry and the reader share. A table property outside its set is an error when the table is opened, naming the property, as a bad boolean or duration was:series-orderused to fall back toautoandfloat-encodingtodictionary. Iceberg's compression codec and level are checked together at creation and byset_properties; set by another engine, a bad one fails writes, never reads. - Changes from the proposal:
dedupis fixed at creation (files written with it off may hold copies, which dedup on read assumes they don't).append-dedupisn't built for the same reason: an append that keeps copies would need its files marked for the reader.format-versionis managed by Timelake (create, upgrade) and can't be set by hand.latest-byandas_of_insertedwait for spec 10. Storage options (timelake_freshness_check, …) stay lake options, documented on the Storage page.
Open questions and issues
None open.
History
- 2026-10-16: built (SPEC.md §21.9)
- 2026-10-19: 35 settings
- 2026-10-19: moved from SPEC.md into its own spec.
- 2026-10-19: was spec 25; specs regrouped one per feature, with milestones on the roadmap.
- 2026-10-20: typed kinds and values; values outside a setting's set are errors when a table is opened