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 → restart →
CREATE EXTENSION session_preload_librariesis not enough- Removing preload after
manage_tableis 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, andON CONFLICTdo 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-equivalentSERIALIZABLEbehavior are unsupported for cold-capable reads. - Partitioned/inherited/foreign/temp/unlogged tables,
TABLESAMPLE, andTRUNCATE ... CASCADEare 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 tableandCOPY table TOcan omit cold-only rows.COPY (SELECT ...) TOcan useKoldMergeScanfor 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.
| Constraint | Hot | Cold | Normal DML checks cold? |
|---|---|---|---|
| Primary key | Yes | Logical winner via merge | No; native check is hot-only |
Non-PK UNIQUE | Yes | No | No |
| Foreign keys | Yes | No | No |
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
TRUNCATEon 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_rowsignored whensince_seq > 0last_rowsmust be<= limit_rows- User-scoped tables: requires
koldstore.user_id, andscope_columnmust be part of the primary key (__clstores PK columns only). Non-PK scope columns are fail-closed forchanges_sinceuntil 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/DELETEthat 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.