Skip to main content
Storage is a separate axis from modality (what the table means) and schema (how columns are shaped). Three knobs exist. You only set two of them; the engine is derived. A fourth catalog field, engine_type (mergetree vs replicated), is a deployment flag for ClickHouse clusters. You do not send it on create. Postgres dataspaces store it as null — it has no meaning there. Overview · Schema

Engine (derived)

You never pass engine on create. Sending the wrong mental model (“I want state on ClickHouse”) is a modality choice (mutable), not an engine override. Edge SQLite is a sync target, not an engine. A state dataspace can be Postgres and edge-replicated.

Storage class — fast vs cheap

ClickHouse only. The Postgres modalities — state, document, and vector — have no storage class. cheap is “this dataspace never needs a hot store.” Point can be cheap from create (map is already uniform). Telemetry can be cheap only after the schema is typed. Mutable, state, document, and vector cannot be cheap.

Tiering — hot vs hot_cold

hot_cold is not the same as cheap:
  • hot_cold + fast = hot MergeTree and a cold Parquet tier (age-out).
  • cheap = Parquet only, no hot MergeTree, no age-out job.
hot_cold requires storage_class: fast. Requesting hot_cold on state, mutable, document, or vector is rejected — mutable cannot collapse versions and tombstones in Parquet, and the other three are Postgres. A document has nowhere to age to: its bytes already sit in object storage. A vector has no time axis to age along, and an aged-out embedding is one that searches would silently stop finding.

Deferred hot_cold on telemetry

Telemetry defaults to jsonb, which cannot tier. If you create with "tiering": "hot_cold" while the layout is still evolving, the platform stores tiering: hot plus a request flag. Age-out turns on only after every field is typed and overflow is drained. Point (map) materializes the cold tier immediately.

Combination matrix

Valid create-time combinations (live modalities): Rejected (400), including:
  • hot_cold + cheap
  • hot_cold on state, mutable, document, or vector
  • cheap on jsonb / hybrid (not frozen)
  • cheap on state, mutable, document, or vector
  • map on anything but point
  • jsonb on mutable
  • map or jsonb on document or vector
  • embedding_* on anything but vector, and chunk_dataspace_id on anything but document
  • sync_targets on document or vector
On document and vector, fields and natural_key are not rejected — the platform replaces them with its own. Sending them changes nothing.

Physical objects

For a fast ClickHouse dataspace the catalog tracks: The Postgres modalities — state, document, and vector — each have a table plus a read view in the project dataspace schema. hot_table / cold_table / storage_class / engine_type are unused (null). Query the dataspace name, not these physical names. Naming a physical view in QueryScript is rejected.

Late data and dropped-rows

The write plane applies a time window to event_ts: Dropped-rows are retained, not deleted. Late ≠ orphan: orphan is schema, dropped is time.

Retention and partitioning

Telemetry and point partition by month of event_ts unless you override partition_by. Mutable uses a single partition so versions of one key cannot split (ReplacingMergeTree only collapses inside a partition). Retention (hot window, cold TTL) lives on the catalog retention object. Age-out for hot_cold is a platform job: it moves parts older than the hot window to the cold table. Cold is immutable — you cannot correct a sample that has aged out.