PostgreSQL 12 shipped the table access method API in October 2019, and the community spent the next six years figuring out what to do with it. The early prediction was that within a few releases we would have a thriving ecosystem of pluggable storage engines — columnar for analytics, undo-log for OLTP, in-memory for hot workloads — and the heap would become “the default, not the only choice.”
That is almost what happened. The ecosystem exists. It is thriving in the sense that there are more credible options now than at any point in the project’s history. It is also messier than the early vision implied, more concentrated around a couple of design philosophies than was expected, and littered with the bones of projects that ran out of funding or hit walls in the API itself. Before the next post in this series puts numbers on any of this, it is worth taking stock of what is actually out there, what each project is trying to do, and where the architectural fault lines run.
This is the survey. The benchmarks come later.
What the TAM API actually gives you
The first thing to understand about alternative storage engines for PostgreSQL is that the table access method API is not a true pluggable storage layer. It is a tuple-shaped abstraction over an interface that was designed around the heap and then partially genericized. There are 38 callbacks in TableAmRoutine. A few are mandatory and behave the way you would expect (scan_begin, tuple_insert, tuple_delete, index_fetch_tuple). A lot of them assume you have a tuple identifier consisting of a block number and an offset, which is fine if your storage is page-shaped and a problem if it is not.
The TAM API also does not extend to indexes. Index access methods are a separate plugin point, predating tableam by many years, and the two are coupled by the TID assumption. If you want a columnar engine that uses something other than (blocknum, offset) to identify tuples, you have to either fake a TID well enough to fool the index AMs or maintain a shadow row store that does have honest TIDs. Most serious columnar TAMs do the latter. This is not optional engineering; it is a constraint of the abstraction.
The result is that “alternative storage engine” in PostgreSQL today means one of four things:
- A heap-shaped replacement that changes the MVCC strategy or durability story but keeps the row-and-tuple-id world intact.
- A columnar TAM that hides a row-shadow under the surface to make the index machinery work.
- A lakehouse-backed TAM that pushes scan and write down to an external columnar engine and treats Postgres as the catalog.
- A domain-specific TAM that solves a narrow problem (time-series, sorted-by-PK, HTAP) and accepts the constraints.
Each category has different reasons to exist and different ways to fail.
Category 1: Heap replacements
OrioleDB
OrioleDB is the most architecturally ambitious living project in the space, and the only one that is seriously attacking the heap on its own ground. The MVCC model is undo-log based — old tuple versions evict to undo chains rather than accumulating in the main heap — which means there is, by design, no bloat in the primary storage. The garbage-collection problem becomes an undo-retention problem instead, which is a different problem with different failure modes but, importantly, not the one PostgreSQL operators have spent thirty years scheduling vacuums around.
OrioleDB also brings copy-on-write checkpoints with row-level WAL, an in-memory caching layer using “squizzled pointers” (a technique that turns pointer dereferences into cache-line-friendly index lookups, when it works), and an experimental S3 mode where WAL archives and undo segments live on object storage. As of 2026 the project is in public beta and not recommended for production. Supabase ships it as an experimental option.
This is the project that, if it succeeds, makes the heap optional in a non-trivial way. It is also the project most likely to discover edge cases in the TAM API that nobody else has hit, because it actually exercises the corners. Watch the bug tracker, not the marketing copy.
ZHeap
ZHeap was EDB’s earlier attempt at the same broad problem — in-place UPDATE with an undo log instead of dead tuples in the heap — and the canonical example of what happens to ambitious storage projects when the funding shifts. The EnterpriseDB repository has been static since 2019. CyberTec picked it up with Heroic Labs funding and has done sporadic work, but the project is, at best, dormant. Mention it for completeness; do not consider it.
The lesson ZHeap teaches is that the undo-log approach to MVCC is a long, expensive engineering project, and the gap between “compiles” and “production-ready” is measured in years of integration work against the rest of the Postgres surface. OrioleDB will face exactly the same gap.
Category 2: Columnar TAMs
This is the densest cluster of activity, and the place where the limits of the TAM API show up most clearly.
Citus columnar
Citus columnar is the survivor lineage. The original cstore_fdw was a foreign data wrapper, predating tableam; it became part of Citus 10 in 2021 and was rewritten on top of the TAM API. It is append-only, supports compression in the 3–10x range on typical analytical data, and gives you the scan-time benefits (chunk-level metadata pruning, selective decompression) you expect from a column store. UPDATE and DELETE are the obvious cost: Citus columnar tables don’t support them at all. Bulk ingest, append, scan. Read-mostly OLAP shapes only.
The project is alive because Citus is alive. Microsoft Azure ships Citus as a managed product, and that is who funds the maintenance. If you are running Citus already for the distribution side, the columnar tables come along for the ride. If you are not, picking columnar-only Citus is a more awkward proposition than it used to be.
Hydra columnar
Hydra is, in effect, a Citus columnar fork with a different go-to-market. The codebase shares ancestry, the column format is similar, and the engineering claim is that Hydra has invested more in vectorized execution and column caching than upstream Citus has. It is positioned as standalone columnar Postgres for analytics, without the distributed-scale-out pieces of Citus.
Whether Hydra and Citus columnar are meaningfully different in 2026 is an empirical question. They were close enough at fork that an honest answer requires actually running them side by side, which is exactly what the next post will do. For now: Hydra exists, it is maintained, and choosing between it and Citus columnar is mostly about which company you want to bet on.
storage_engine
storage_engine 1.0.7 (Saulo Benvenutti, April 2026) is the new entrant. It ships two TAMs in one extension: colcompress (column-oriented, vectorized execution, chunk-level min/max pruning, MergeTree-style ordering) and rowcompress (row-oriented, batch-compressed, with DELETE/UPDATE via deleted bitmasks and an LRU decompression cache). The 16/17/18 compatibility matrix is the right one. The author’s microbenchmarks claim 10x aggregation speedups and 3–5x compression on a 1M-row workload, which is in the right ballpark for the category and worth verifying independently.
The architecturally interesting choice here is the row-oriented compressed TAM. This is a less explored design point — most projects pick column-oriented for analytics or stay with the heap for OLTP — and it is a credible answer for “I want compression on a workload that still has random updates.” The MergeTree-shaped ordering on the columnar side is a clear influence from ClickHouse. Both TAMs are maintained by a small team, and the extension is young enough that I would not put it under critical-path workloads yet, but it deserves benchmarking.
pg_cryogen
pg_cryogen is the small, focused, append-only compressed TAM from Adjust, built specifically for archive-shaped workloads where the data is written once and queried occasionally. It compresses well, scans well, and does not pretend to do anything else. Maintenance is sporadic; the design is honest about its narrow scope. If your problem is “I have terabytes of immutable event data and I need it in Postgres for SQL access without paying full heap storage costs,” pg_cryogen is worth looking at.
Category 3: Lakehouse-backed TAMs
This is the category that has come into focus in the last eighteen months, and it is where most of the design innovation is happening — partly because the TAM API turns out to be more useful for “Postgres as a query frontend over an external columnar engine” than it ever was for “Postgres has its own column store.”
pg_mooncake
pg_mooncake creates a columnstore mirror of your Postgres tables in Iceberg or Delta Lake format, either on local disk or on S3-compatible object storage. The TAM stores only metadata in Postgres; the actual data is Parquet plus Iceberg/Delta manifests. Queries against pg_mooncake columnstore tables are accelerated by an embedded DuckDB. Transactional inserts, updates, and deletes against the columnstore are supported.
The right way to read this is as a Postgres-native answer to the same question Snowflake’s pg_lake is answering: how do you give a Postgres user lakehouse storage without making them leave Postgres? pg_mooncake answers it through the TAM API and an in-process DuckDB; pg_lake answers it through a separate pgduck_server process and a foreign data wrapper. Different boundaries, different trade-offs, same destination.
pg_mooncake is on Neon as a Beta extension. The implementation has an open issue to “implement full PG TAM,” which tells you something useful about the maturity of the integration: not every TAM callback is implemented, and the authors are honest about which corners are sharp.
pg_duckdb
pg_duckdb — the official DuckDB-team extension — embeds DuckDB in the Postgres backend and exposes a duckdb table access method (CREATE TABLE foo (...) USING duckdb). It can also query remote Parquet, CSV, JSON, Iceberg, and Delta files directly without needing a TAM-resident table at all.
The architectural question with pg_duckdb is the one I mentioned in the recent pg_lake digest piece: DuckDB is multi-threaded, has its own allocator, and its own ideas about what an error is. Embedding it in the same process as a Postgres backend is a non-trivial integration, and the project documentation makes the trade-offs explicit. For “I want analytical execution against this data and I do not want a separate process,” pg_duckdb is the answer. For “I want a robust production storage engine,” it is positioned more as a query-acceleration layer than a primary store.
ParadeDB / pg_search
ParadeDB is interesting for a slightly different reason: it is the only project in this survey that has ported an external file format directly onto Postgres block storage. Tantivy’s full-text search format normally lives in its own files; ParadeDB’s pg_search extension puts it inside Postgres pages, which means it participates in WAL, replication, and the buffer cache. It also uses columnar storage internally for fast fields (numeric and categorical) for filtering and sorting, even though it is not what most people would call a “columnar storage engine.”
pg_analytics, ParadeDB’s DuckDB-on-Postgres extension, has been discontinued and rolled into pg_search. Worth knowing if you’ve been tracking the project. The block-storage technique is the more interesting story.
Category 4: Domain-specific TAMs
TimescaleDB Hypercore (RIP)
The cautionary tale. TimescaleDB 2.18 introduced a hypercore TAM that handled compressed columnar storage with transparent decompression, allowing the standard Postgres index machinery to work against compressed chunks. It was the most ambitious use of the TAM API by a major project. The Timescale team deprecated it in 2.21 and sunset it in 2.22 (September 2025), having decided the experiment “did not show the signals hoped for.” Compression is still there; it is back in the columnstore-without-TAM machinery, accessed through sparse indexes and the existing chunk-management code.
The lesson is worth dwelling on. Timescale has more engineers and more funding for storage work than almost any other project in this list. They built a TAM, shipped it, ran it in production, and walked it back. The reasons they walked it back are exactly the reasons the TAM API is harder than it looks: tuple-shaped abstractions over column-shaped data require either a row shadow (which you then have to keep in sync) or a forest of special cases (which you then have to maintain across upgrades). Timescale’s post-mortem is implicit but readable in the changelogs.
If you are evaluating columnar TAMs in 2026, “Timescale tried this and gave up” should be in your prior. Not as a death sentence — Timescale’s specific use case has more constraints than a general-purpose columnar TAM has — but as a calibration point.
pg_sorted_heap
pg_sorted_heap 0.13.0 (May 5, 2026) is brand new and worth a serious look. It maintains physically sorted heap storage by primary key with per-page zone maps for range-predicate pruning, paired with a planner-integrated HNSW index AM (sorted_hnsw) and svec/hsvec vector types. Version 0.13.0 promotes a “fact-shaped” GraphRAG contract — (entity_id, relation_id, target_id) clustered facts — to its stable surface.
The architectural choice is interesting: rather than building a general-purpose TAM, the author built a TAM optimized for a specific shape (sorted-by-PK, range-predicate-heavy, with vector retrieval) and integrated it with the planner and an HNSW AM in a way that pgvector does not. The microbenchmarks claim parity-or-better with pgvector halfvec at 100% Recall@10, which is a credible claim if you accept the workload shape. This is the right way to use the TAM API: pick a constraint, optimize hard for it, and tell the user honestly what you are good at.
cstore_fdw (historical)
cstore_fdw is the FDW-based ancestor of Citus columnar. It still exists; it should not be used for new work. The repository itself recommends Citus columnar instead.
Honorable mentions and dead ends
ZedStore, the VMware/Pivotal columnar TAM project from 2019, never reached production maturity. The codebase is around for archaeological purposes. The design ideas (hierarchical organization of column data, trying to be HTAP-compliant out of the gate) influenced later projects but the project itself is a dead end.
pg_strom is a TAM-adjacent project doing GPU acceleration; it is alive but its target use case is narrow enough that it does not belong in a general storage-engine survey.
EDB’s Advanced Storage Pack and similar enterprise-only modules from various commercial vendors exist. They are out of scope for this survey because the source is not available and the design choices cannot be inspected.
What this means for picking one
The honest answer is that the choice in 2026 is more constrained than the marketing materials suggest. A few patterns are clear.
If you have a primary OLTP workload and you are looking at storage engines because you have a bloat or write-amplification problem, the answer is probably not an alternative TAM. OrioleDB is the most credible undo-log option, but it is in beta and you should treat it as such. The other heap-replacement candidates are not in active development. The pragmatic answer is to fix your VACUUM strategy, partition aggressively, and wait.
If you have an analytical workload that is co-located with OLTP — the HTAP shape — you have to pick where you draw the boundary. Citus columnar and Hydra are credible read-mostly columnar options for data you are not going to update; pg_mooncake and pg_duckdb are credible options if you are willing to push execution out to DuckDB and accept the operational complexity that comes with it. storage_engine is a young challenger that deserves benchmarking but should not yet be in the critical path.
If your workload is time-series, you should be on TimescaleDB without the deprecated Hypercore TAM, using the native columnstore. Timescale is the right answer for time-series and has been for years.
If your workload is sorted-by-PK with vector retrieval, pg_sorted_heap is worth a benchmarking pass. It is young; it is also one of the few projects in the survey that is honest about its scope and has a planner-integrated index AM rather than a bolted-on opclass.
The follow-up post will run the same workloads — ingest, point-lookup, range-scan, vectorized aggregate, and a write-heavy mixed shape — against the heap, OrioleDB (with caveats), Citus columnar, Hydra, storage_engine, pg_mooncake, and pg_sorted_heap. The numbers will say more than this taxonomy can.