Skip to Content
Status & Limits

Status & Limits

KoldStore is in early development and is not production-ready. Manage, flush, auto-flush, hot/cold query, and changes_since work. Recovery, backup/restore, compaction, schema evolution, PK changes, and export/import are still hardening.

Shared preload is mandatory

shared_preload_libraries must include koldstore. Without it, backends can silently run ordinary heap scans and return hot-only rows after flush.

  • Install → set preload → restartCREATE EXTENSION
  • session_preload_libraries is not enough
  • Removing preload after manage_table is unsupported

Check: SELECT koldstore.preload_status();

PostgreSQL semantics across tiers

The original relation remains a PostgreSQL heap for hot rows. Cold rows are materialized by an experimental custom scan, so full heap semantics do not automatically extend to Parquet:

  • WAL capture is asynchronous and committed-only. It does not provide read-your-own-uncommitted-writes when an older version of the key is cold (#121 ).
  • wait_for_async_mirror() fences commits up to a captured WAL boundary. Run it before acquiring a fixed snapshot; it cannot advance an existing snapshot.
  • Standard UPDATE/DELETE, native PK checks, and ON CONFLICT do not target or constrain rows that exist only in cold storage (#122 ).
  • Cold rows have no heap ctid, xmin, row lock, or SSI predicate lock. System columns, SELECT ... FOR UPDATE/SHARE, and PostgreSQL-equivalent SERIALIZABLE behavior are unsupported for cold-capable reads.
  • Partitioned/inherited/foreign/temp/unlogged tables, TABLESAMPLE, and TRUNCATE ... CASCADE are outside the documented preview contract (#125 ).
  • Renaming a table/schema after cold publication is unsafe until cold object identity is independent of mutable names (#64 ).
  • Schema evolution is fail-open for unsupported changes today, and old Parquet rows do not yet reproduce every new DEFAULT/NOT NULL rule (#123 ).
  • pg_dump --data-only -t table and COPY table TO can omit cold-only rows. COPY (SELECT ...) TO can use KoldMergeScan for supported query shapes (#126 ).

User-scoped mode is application-context filtering, not authentication. koldstore.user_id is user-settable and must be bound by a trusted connection layer. The generated RLS policy is permissive, so another permissive policy can broaden the combined rule. Privilege hardening for the management API is tracked in #120 .

Planner row estimates can describe the hot child rather than the logical hot+cold row set; cold-aware statistics and costing are tracked in #124 .

UNIQUE and foreign keys

PostgreSQL UNIQUE and FK constraints on managed tables are enforced on the hot heap only. After flush, cold values live in Parquet without a global hot+cold constraint layer.

ConstraintHotColdNormal DML checks cold?
Primary keyYesLogical winner via mergeNo; native check is hot-only
Non-PK UNIQUEYesNoNo
Foreign keysYesNoNo

When hot_row_limit is set, manage_table rejects tables that already have non-PK UNIQUE constraints or foreign keys.

Indexes and extensions

PostgreSQL indexes (including pgvector HNSW/IVFFlat and similar) cover hot rows only. Flushed rows lose heap index entries. There is no automatic Parquet index translation yet. The vector type is not in the v0.1 supported type matrix.

Cold storage availability

If cold storage is unavailable, managed queries that need cold segments fail. KoldStore does not silently return hot-only results. koldstore.enable_merge_scan = off also errors rather than allowing an incorrect heap-only read.

Capture limits

  • WAL-only capture (no trigger mode)
  • Primary-key updates rejected
  • TRUNCATE on async managed tables rejected
  • Bounded mirror lag unless you call wait_for_async_mirror()
  • Scoped physical cold folders still incomplete vs registration templates
  • Slot invalidation/failover rebuilding is not hardened (#127 )
  • Large-transaction streaming and database-wide apply isolation are not hardened (#128 )

Change feed limits

  • Latest-state catch-up, not full event history
  • Positive cursors older than retained cold floor → retention-gap error
  • last_rows ignored when since_seq > 0
  • last_rows must be <= limit_rows
  • User-scoped tables: requires koldstore.user_id, and scope_column must be part of the primary key (__cl stores PK columns only). Non-PK scope columns are fail-closed for changes_since until mirror scope materialization lands.

Unmanage, not disable

Use koldstore.unmanage_table(...). Do not set koldstore_enabled = false. Storage binding is immutable after manage.

Not shipped

  • Packaged extension upgrade edges (ALTER EXTENSION … UPDATE)
  • Cold DML helpers and cold-only UPDATE/DELETE that mutate Parquet in place
  • backup_manifest / validate_cold_storage / polished export-import
  • Compaction and schema evolution tooling
  • Coordinated backup/restore/PITR across PostgreSQL and cold objects

When to wait

Prefer waiting if you need global uniqueness across tiers, cold lookups as fast as B-trees, or turnkey PITR that spans heap + object storage out of the box.

When to try

Local or staging workloads that grow forever (messages, audit, AI memory) where shrinking the hot heap and indexes is the main goal, and you can tolerate the current ops boundaries.

Last updated on