IsomerDB
rev 0.28.0 is out, see what changed

One database.
Write once, read in any projection.

Keep your SQL tables as the single source of truth. Documents, search indexes, vectors and caches are projections of those tables that the database builds, shards and keeps consistent on every write. Start by attaching to the Postgres you already run, with no migration.

parts list
ItemPart
1TablesThe source of truth. Full SQL, joins and transactions, sharded across nodes.
2Projection engineRe-derives what each write touches, holds every projection to its freshness, audits for drift.
3ProjectionsDocuments, search, vectors, caches, events and analytics, each a query over the tables.
4InterfacesPostgres wire, MongoDB wire and HTTP. A write token makes any of them read your writes.
5Your appsOrdinary drivers, unchanged.
6AttachFollows the Postgres you already run until you decide to cut over.

exploded view of the stackin red: each write, re-deriving the projections it touches

your apps SQL MongoDB wire HTTP docs search vectors cache events analytics projection engine node 1 node 2 node 3 your Postgres attach 1 2 3 4 5 6
Sheet1/6TitleThe stackRev0.28.0

Five stores, and a pipeline for each

Products start on Postgres. Then come MongoDB for nested documents, Elasticsearch for search, Redis for hot keys and a vector store for embeddings. Every sync job between them is a place where data goes missing or stale, and no transaction spans them.

Postgres MongoDB Elasticsearch Redis Vector store CDC job ETL invalidation embed script stale? no transaction spans these
Today: four copies, four ways to drift.
ISOMERDB SQL tables documents search vectors cache SQL MongoDB HTTP every write, one transaction
With IsomerDB: one store, projections kept by the database.
Sheet2/6TitleThe problemRev0.28.0

From the Postgres you run today to one database

You don't migrate on day one. IsomerDB follows your existing database, builds what you read, and takes over as the primary only when you decide to cut over.

  1. Attach

    Point IsomerDB at your Postgres. It checks every table for keys and replica identity, copies it, then follows the change stream. Table names stay the same, and nothing is migrated.

    SELECT * FROM isomer.attach(
      'shop', 'host=... dbname=... user=...');
  2. Declare a projection

    Write the shape you want to read as a SQL query. The database builds it, shards it beside the rows it comes from, and certifies it.

    SELECT isomer.create_projection(
      name => 'customer_profile', kind => 'document',
      key  => 'id', query => $$
        SELECT c.id, jsonb_build_object('name', c.name,
          'orders', (SELECT jsonb_agg(o.status)
                     FROM orders o WHERE o.customer_id = c.id)) AS doc
        FROM customers c $$);
  3. Write as usual

    Each write re-derives only the keys it touches. Aggregates across shards are maintained incrementally from row deltas, and a cost model picks the strategy.

    UPDATE customers SET name = 'Asha' WHERE id = 42;
    SELECT isomer.last_write_token();  -- carry it to the read
  4. Read it anywhere

    Read the projection from SQL, as a MongoDB collection, or over HTTP. Pass the write token and the read waits for exactly that write. Causally consistent MongoDB sessions do it with no code changes.

    # MongoDB driver, unchanged
    db.customer_profile.findOne({ _id: 42 })
    
    # HTTP, waiting for your write
    curl -H "X-Isomer-After: $TOKEN" \
      localhost:8080/v1/projections/customer_profile/docs/42
  5. Cut over

    When you're ready, IsomerDB freezes the source, catches up, copies sequences and defaults, and becomes the primary. It can replicate back for a rollback. A cutover plan lists everything left to recreate first.

    SELECT * FROM isomer.cutover('shop',
      rollback_conninfo => 'host=... user=...');
    SELECT isomer.alter_projection('customer_profile',
      freshness => 'sync');
Sheet3/6TitleHow it worksRev0.28.0

Every projection has a speed limit

You choose how fresh each projection must be, and the database holds to it.

Sync

Updated inside the writing transaction. A read never sees the table and the projection disagree. Available once IsomerDB is the primary.

Bounded

Maintained right after the write, within a lag budget such as 2 seconds. A breach raises an alert, and a read that passes a write token waits until that write has landed.

LAG · SECONDS budget 2 s 0.6 s now
order_search: bounded, budget 2 s
Sheet4/6TitleFreshnessRev0.28.0

Seven kinds of projection, one set of tables

Each one is a query over the same tables. Drop it and the data is still there; change the query and it is rebuilt.

Document

read as a MongoDB collection

Nested documents built with joins and subqueries, kept current on every write.

Search

BM25 from SQL

Full-text search ranked with BM25, over joins across sharded tables.

Vector

nearest neighbour in SQL

Only rows whose text changed are re-embedded, with a hosted model or bge-small on your own CPU.

Cache

hot keys, by key

Key-value entries derived from the tables, so there is nothing to invalidate by hand.

Event

a change feed, or live over HTTP

Domain events from row changes, such as order_paid, in commit order per row.

Analytics

columnar tables

Aggregates maintained incrementally: a change rewrites only the days or groups it reaches.

Edge

replicas close to your users

Projections served from memory over HTTP and MongoDB, fed by the change feed.

Proposed

by the database itself

It reads its own query log, proposes the projections and shard keys it should have, and routes a slow query only after checking the rows match.

Sheet5/6TitleProjectionsRev0.28.0

The usual stack, next to IsomerDB

The same data kept in Postgres, MongoDB, Elasticsearch, Redis and a vector store, synced by CDC pipelines, against one database that keeps its own projections. Every claim on the right is checked while it runs.

QuestionPostgres + sync pipelinesIsomerDB
Does a write land everywhere at once?No. Each store commits on its own, so a crash between them leaves the copies disagreeing.Yes. Sync projections change in the writing transaction; bounded ones follow within a lag budget you set.
Can I read my own write from another store?Not reliably. A read can come back stale until the consumer catches up, and nothing tells you when it has.Yes. Pass the write token and the read waits for that write, over SQL, MongoDB or HTTP.
How fresh is each copy?Whatever the pipeline manages that day. Seconds is normal; a backfill can take it to minutes.Declared per projection, measured all the time, and an alert fires on a breach.
How do I find a copy that has drifted?Usually a customer reports it.The live auditor recomputes samples from the tables and pages on-call on any difference.
How do I know it's correct?Each store is tested on its own. Nothing tests the pipelines between them.Each projection carries a certificate of the rules it relies on, and Jepsen runs nightly with nodes killed, paused and partitioned.
What do I operate?Five databases, Kafka, connectors and the consumers you wrote.One database.

IsomerDB column: Jepsen nightly run of 23 October 2026, five workloads valid (isolation across shards, read-your-writes over SQL and MongoDB, incremental and subquery projections). The workloads have found and fixed three bugs so far.

Sheet6/6TitleComparedRev0.28.0

take it for a drive

Running in one command

Pull the released images and you have Postgres, the MongoDB gateway and the console on your machine. For production, the Kubernetes operator runs hot standbys, fails over, backs up and upgrades.

# needs Docker with Compose v2
git clone https://github.com/Ash20pk/IsomerDB.git && cd IsomerDB
ISOMER_REGISTRY=ghcr.io/ash20pk/ ISOMER_TAG=0.28.0 \
  docker compose up -d --no-build
./scripts/smoke.sh
psql postgres://postgres:isomer@localhost:5432/postgres