Multi-writer PostgreSQL on shared storage
Every server writes. Lose one, keep every commit.
Synod runs up to eight PostgreSQL writers on the same tables and one shared copy of the data, the way Oracle RAC does. When a writer fails, the others lock it out and carry on without any central coordinator. No acknowledged transaction is lost, and none is written twice.
Built from Neon's open-source storage, which we maintain in our own repository. Synod adds the multi-writer engine, the writers' own cluster membership, storage that splits into shards and fails over to a standby, and the installer and control plane that run it on your servers.
- writer 3 stops responding. Writers 1 and 2 keep committing. The control plane is switched off for the whole test.
- writer 1 has had no heartbeat answer for 30 s. It holds a majority with writer 2, so it locks writer 3 out of the log servers.
writer 3 evicted by writer 1 in 0.03 s (no coordinator)
- writer 3 wakes up alone and may not evict anyone.
this side has 1 of the 3 members
- writer 3 finds itself fenced and shuts down. Its host restarts it.
this primary is fenced: safekeeper ... is in term 4, after our last elected term 3
- writer 3 rejoins, and the others hand back the pages it manages.
writer 3 rejoins ... after 18.6 s evicted
No split brain. 31,063 changes acknowledged, 0 missing, the same rows on all three writers.
The evidence so far
acknowledged changes missing out of 309,727, across nine writer failures
AWS, 3 writers, Oct 28random faults with every check consistent, 8 of them with a second fault at once
AWS, 60 minutes, Oct 30tables identical on all three writers after PostgreSQL's own regression suite
3 writers, Oct 30for every writer to move to a standby page server after one was killed under load, with 0 errors and nothing lost
AWS, 3 page servers, Oct 31Prototype software, tested in October 2026 on small machines. The full results and how we test are on the Results and Testing tabs.
The gap
PostgreSQL has no open-source answer to Oracle RAC
One writer at a time
A PostgreSQL cluster has one server that can write. Each replica keeps its own full copy of the data, and promoting one when the writer dies is left to outside tools.
Failover is where data goes missing
If the old writer is only unreachable rather than dead, it can keep accepting writes nobody will ever see. Preventing that split brain is the hardest part of any failover design.
RAC users have nowhere to go
Teams that run Oracle RAC for several writers on shared storage have no like-for-like open-source PostgreSQL option to move to.
Who it's for
Built for teams that can't lose a commit
Teams running Oracle RAC
Several writers on shared storage and safe failover, on PostgreSQL, without Oracle's per-processor licence for the database and its RAC option.
How it compares with PostgreSQLPostgreSQL teams that can't afford a gap
Any writer serves, a failed one is fenced and restarts by itself, and acknowledged commits survive. It runs on your own servers or cloud account.
How it worksMigration partners
The RAC databases your Oracle-to-PostgreSQL projects had to leave on Oracle. Bring one, and we run a joint pilot; the migration work stays yours.
Work with usRunning Oracle RAC, or PostgreSQL you can't afford to lose?
We're looking for a few design partners for a free pilot on a non-production copy, with direct support from the team that built it.
Technology
Writers share every table and coordinate among themselves
Database servers hold no data of their own. Every writer's changes go to three log servers and count once two of them have them on disk, and page servers rebuild any page on request, each with a standby that takes over if it fails. What Synod adds sits between the writers.
Three mechanisms, borrowed from RAC and rebuilt for PostgreSQL
-
Pages move between writers
Each page has a managing writer, picked by hashing. A writer that needs to change a page asks that writer for the right to, and gets the current copy straight from whoever holds it, the way RAC's cache fusion works.
-
One commit order, no central clock
Each writer numbers its own commits on a clock the writers keep in step. Every other writer hears of a commit before the client is told it succeeded. Old row versions live in an undo log, so readers never wait for writers.
-
The writers police each other
Writers check on each other over the network and through the log servers. A majority locks out a silent writer by starting a new term on its log, so it can never commit again. When it comes back it rejoins on its own.
What's Neon's, what's ours
Built on Neon's storage, extended to many writers on the same tables
From Neon's open-source storage
Apache 2.0, owned by Databricks
- Shared storage: log servers, page servers and S3
- A storage controller that can split a database into shards and keep standby copies
- A new writer starts without copying the database
- Election terms on each writer's log, which lock out an old writer
- One writer per database
What Synod adds
Our own work
- Up to eight writers on the same tables. A table engine with an undo log, page transfers between writers, one commit order across them, and row locks and deadlock detection that span writers. Tested with three writers on separate AWS machines.
- Membership without a coordinator. Heartbeats over the network and through storage, majority rule for evictions, fencing through storage terms, and restart and rejoin without outside help.
- Schema changes on every writer at once. ALTER TABLE, CREATE INDEX and table rewrites such as CLUSTER and VACUUM FULL commit on all writers together, while every writer keeps changing the table.
- Direct, encrypted connections. Clients list every writer in one connection string, as RAC clients do. Each writer has its own TLS certificate and checks passwords itself; a password change reaches every writer together.
- Backups and restores for the whole cluster. pg_dump from any writer gives one consistent snapshot. A restore point every minute, where no commit crosses between writers, lets a whole cluster be restored to that moment in seconds.
- An installer and control plane for your own servers. Neon's own control plane is not open source. Ours sets up a cluster on separate machines, splits it into writers and moves a writer off a dead host.
- Storage that scales out under every writer. With two or more page servers each database gets a standby, and every writer follows a move to it. A database splits into shards and a balancer spreads them across page servers while it runs. The map of page versions grows with the data. Tested on AWS with three page servers.
- The test harness. Ledger checks across crashes and network splits, an isolation checker, randomized fault runs, and a replay for every bug found.
Deployment
Runs on your servers, in your data center or cloud account
One inventory file
List the machines and their roles. The installer sets up the log servers, page server, writers and control plane over SSH, as system services.
No single point of control
The control plane runs with a standby that takes over in about 3 s, and the writers keep committing while it is down.
Tested on AWS
Installed on 8 machines and checked through a control plane failover, a writer kill, a machine reboot and a full stop and start, with nothing lost. Ubuntu 24.04 today.
Synod RAC vs PostgreSQL, October 31, 2026
To your application, every writer is a PostgreSQL 17 server
Applications connect to any writer and read and write the same tables, in any database of the cluster. Ordinary SQL, transactions, backups, restores and failover behave as they do on one PostgreSQL server. Two features are refused, a few behave differently, and Synod is still a prototype.
Works like PostgreSQL
SQL and transactions
Queries, joins, constraints, foreign keys, triggers, sequences, partitions, MERGE, ON CONFLICT; READ COMMITTED and REPEATABLE READ. SERIALIZABLE works too, differently (below).
Indexes
B-tree, GIN, BRIN, GiST, SP-GiST and hash. CREATE INDEX, REINDEX and DROP INDEX, also CONCURRENTLY.
Schema changes
Sent to any writer and made on every writer, while all of them keep working, even while one writer is down.
Databases and tablespaces
CREATE and DROP DATABASE on any writer; every writer serves every database. Tablespaces and SET TABLESPACE.
Maintenance
VACUUM, VACUUM FULL, CLUSTER and REINDEX, of one table, partitioned tables, a schema or the whole database.
Large rows
Rows up to a page (about 8 KB) stored whole; larger values compressed or moved out of line, as in PostgreSQL.
Cluster-wide features
Advisory locks, LOCK TABLE, LISTEN and NOTIFY and prepared transactions work across all the writers; so does subscribing to another database.
Backups and restores
pg_dump, parallel too, and pg_restore. One database restored to any moment; a whole cluster to a restore point, one a minute.
Crash safety
No acknowledged commit lost when one writer, or every writer at the same instant, is killed under load.
PostgreSQL's own regression suite run on a three-writer cluster: afterwards every table is identical on all three writers and 424 B-tree indexes pass a full check against their tables. 62 of its 225 tests print something different from one PostgreSQL server: plans and row order, message wording, and the differences below.
Works, but differently
| Area | One PostgreSQL server | Synod RAC |
|---|---|---|
| A server fails | Clients wait for failover to a replica | The other writers pause up to about 30 s, then carry on; the failed writer restarts and rejoins by itself |
| Writes to the same rows from several writers | Local | A network round trip when another writer changed the data last |
| SERIALIZABLE | Serializable snapshot isolation | Strict locking: also serializable, with more waiting, and a different transaction may be the one that fails |
| Shared row locks (FOR SHARE, some foreign-key checks) | Several transactions share the lock | Some wait for each other: slower, not wrong |
| Statistics views, temporary and unlogged tables | One server | Kept per writer |
| SET LOGGED / SET UNLOGGED | The table keeps its rows | An unlogged table is each writer's own copy; SET LOGGED keeps the rows of the writer that runs it |
| Prepared transactions | Always allowed | Refused if the transaction holds advisory locks, LOCK TABLE or SERIALIZABLE locks, or made a schema change |
| Cancelling a COMMIT | Possible | Waits until storage confirms the commit |
| Publishing changes (logical replication, CDC) | Subscribers connect to the server | Subscribers connect to synod_cdc, a small server in front of the cluster that sends every writer's changes in commit order |
| CREATE / DROP DATABASE | Any time, from any template | Made or dropped on every writer at once; every writer must be up; template0 or template1; up to 16 databases |
| Tablespace location | Files go in that directory | Accepted but not used: the data stays in the cluster's shared storage |
| GIN fastupdate | New entries wait in a pending list | Accepted; entries go straight into the index (same results, inserts a little slower) |
Refused, with a clear message
- Switching a table to another storage format (SET ACCESS METHOD): shared tables use Synod's own format.
- Renaming a database, or making one from a template other than template0 or template1.
Limits today
- A prototype. Tested with up to three writers (eight allowed), on small machines in one AWS zone, in runs of up to 90 minutes. No production use yet.
- A failed writer pauses the others. Commits on the other writers wait up to about 30 s (a setting), until it is evicted.
- Page servers. With two or more, every database has a standby; when a page server fails, its work moves to the standby in about 12 s, and reads that need it wait until then. Tested with three page servers and a few GB, not yet at multi-terabyte sizes.
- Cold scans. Reading a whole shared table that is not in memory runs at about 200 pages a second per writer on small machines, until read-ahead is built.
- A private network. Traffic between the writers is not encrypted yet, and storage trusts its network.
- Upgrades. All writers are upgraded together; no rolling upgrades yet.
Several databases per cluster, SET LOGGED / UNLOGGED, rows up to a page, publishing changes through synod_cdc, and the maintenance statements once refused were added on October 30 and are in the installer since October 31; the AWS suite passed on that build. The features themselves are tested on a local three-writer cluster.
AWS results, October 28–31, 2026
Kill a writer or a page server: the ledger stays exact
Three writers on three machines, three log servers, one to three page servers and S3: 8 AWS machines with 2 vCPUs and 8 GB each. Clients on every writer insert, delete and re-key rows of shared tables. After each failure, every acknowledged change must be there, the rows must be the same on every writer, and every index must match its table.
Storage scale-out, October 31, 2026
Split the storage, kill a page server, add one: nothing lost
Three writers on separate machines changed shared tables, about 174 times a second each, in a 1 GB database of 900,000 rows, while we split its storage, killed a page server and added a new one. Disks and the bucket were encrypted with AWS KMS.
to split the running database into 4 shards; every writer was reading the new shards 5.2 s after the request
from killing the page server that held shard 0 until every writer read it from the standby, which took over after 10.1 s
errors in the load, committed changes missing and lost updates; the data identical on all three writers and every index checked clean
| Step | What happened | Time |
|---|---|---|
| Split into 4 shardsUnder load | The database split on page server B, and every writer switched to the new shards. | 1.8 swriters 5.2 s |
| Shards spreadBy the balancer | Shard 0 moved to page server A; every shard kept a standby copy on the other server. | 16 s |
| Page server killedkill -9, kept down | The standby on B took over shard 0. Reads that needed it waited meanwhile, and the load saw no errors. | 10.1 swriters 12.3 s |
| Killed page server restarted | It came back as a standby. | — |
| Third page server addedWhile the load ran | The balancer moved shard 0 to it, and the writers followed. | 82 s |
The first storage run on AWS found that writers restarted earlier in the test did not follow storage moves. That is fixed, and the results above are from the rerun on a fresh cluster.
Writer failures, October 28, 2026
acknowledged changes missing out of 309,727, across nine writer failures with the control plane switched off
for the remaining writers to lock out a silent one, once its 30 s heartbeat timeout ran out
for an evicted writer to come back, rejoin and take back the pages it manages, with no operator involved
Every failure, and what the ledger showed
| Failure | What happened | Acknowledged | Missing |
|---|---|---|---|
| Writer killedEach of the three in turn | Its host restarted it in place. The other writers forgot what it held and kept going. | 138,705 | 0 |
| Writer frozen for 45 sEach of the three in turn | Evicted after the 30 s heartbeat timeout and locked out of storage. On waking it found itself fenced, restarted and rejoined 17–19 s after its eviction. | 89,436 | 0 |
| Network splitOne writer cut off from the other two | The majority evicted it. It rejoined about 20 s later, once the network healed. | 55,512 | 0 |
| Page transfer service frozen for 45 sPostgres still up | Evicted like a frozen writer. Rejoined 7.5 s after its eviction. | 26,074 | 0 |
| Six table rewrites under loadALTER COLUMN TYPE, CLUSTER, VACUUM FULL | Each took 2–28 s and ran on all three writers together while every writer kept changing the table. | 8,847 | 0 |
| Schema changes from every writerUnder load | System catalogs identical on all three writers afterwards. Index cleanup: 139,161 rows and 139,161 index entries on each writer, every index verified. | — | 0 |
The heartbeat timeout is a setting (30 s by default). A writer that is killed rather than frozen is restarted in place without waiting for it: its host notices the exit and the other writers drop what the old run held.
Rerun on October 30 on the finished engine, installed with our installer on 8 AWS machines: 18 of 18 three-writer tests passed, with every acknowledged change present. Run again on October 30–31 with three page servers: the nine writer faults, schema changes under load, index checks and table rewrites all passed, with no committed change missing.
Watch the three-writer tests
A freeze, a kill, a network split and six table rewrites, in two minutes
Earlier: one writer per database (stages 1 and 2, October 25)
| Failure | What happened | Acknowledged | In the ledger |
|---|---|---|---|
| Writer cut off from storageClients can still reach it | Replacement writing after 4.4 s. The old writer committed nothing and was refused when the network healed. | 65,494 | 65,502+8 in flight |
| 2 of 3 log servers stopThen restart | Writes pause with no errors and resume about 5 s after the servers return. | 148,512 | 148,512 |
| Page server stops for 20 s | No drop in throughput. | 154,802 | 154,802 |
| Database moved to another serverPlanned, under load | Writing on the new server after 16.3 s with default storage settings, 1.3 s with storage tuned for fast starts. | 22,363 | 22,363 |
A bank workload: each transaction moves money between two accounts and writes a ledger row. "In flight" commits were running when a server died, so the client never got an answer; they may be in the ledger, and acknowledged commits always are.
Speed
One writer, 16 clients over the network: 43% of plain PostgreSQL 17 on the same machine type, with every commit stored on two other machines before it is acknowledged.
Four databases with writers on two machines, sharing one storage service. A second writer machine adds throughput.
Two writers in one database, each owning its own tables and reading the other's: our first multi-writer prototype.
Throughput with every writer changing the same tables has not been tuned yet. That is the next piece of work.
How we test it, October 31, 2026
We try to break it on purpose, then replay every break
A multi-writer database fails in rare timings that ordinary tests never hit. So we run eight kinds of test against three-writer clusters, and every bug they find becomes a replay that forces that exact timing.
PostgreSQL's own test suites
The regression and isolation suites run against a three-writer cluster. Afterwards all 541 tables are identical on the three writers and 424 B-tree indexes pass a full check against their tables.
62 of 225 regression tests print something different: plans, row order, wording and the differences on the vs PostgreSQL tab
Isolation under faults
Clients on every writer append to lists and read them back at REPEATABLE READ while writers are killed, frozen or cut off. Each history is checked for dirty or lost writes and for lost or diverging appends.
Only write skew found, which REPEATABLE READ allows
Randomized fault runs
Runs of 40 to 80 minutes that pick a failure at random, sometimes two at once: a writer killed, frozen or cut off, its page transfer service stopped or frozen, a log server or the page server killed. Under load, on one machine and on 8 AWS machines.
AWS, October 30, finished engine: 30 random faults in 60 minutes, 8 of them with a second fault at once, 30 of 30 consistent. Earlier, a 36th fault exposed the stale index entry below
A replay for every bug
Each bug is made to happen on purpose with test pause points. A fix counts only when the replay keeps the data and a build without the fix loses it.
12 replays; each fix, built without it, fails its replay
Every writer killed at once
All three writers killed in the same instant, three times, while clients move money between accounts: the power failure of a whole cluster.
Nothing acknowledged missing, no lost update, 3 of 3
Backups and restores
A dump from any writer under load is one consistent snapshot, and parallel dumps restore into one database or a new cluster. A whole cluster restored to a restore point has every transfer acknowledged before it, and none started after.
Cluster restored in 2.0 s; a restore point pauses commits for 0.1–0.4 s
Storage faults
A page server killed under the shared-table load, the database split into shards and moved between page servers, and a new page server added, while every writer keeps changing the same tables.
AWS, October 31: split in 1.8 s, writers on the standby 12.3 s after the kill, 0 errors, nothing missing
Page version map stress
Each writer keeps a map of page versions that grows with the data. Stress runs on a build with small buckets add 100,000 entries per writer, about 4,300 splits each, under load and with a writer killed.
6 of 6 stress checks and 10 of 10 map checks pass, including reads by another writer from storage
What the tests found, all fixed
After an eviction, a page's new managing writer could reuse a version number already given out, so a stale copy could later win.
Fault test, one machineA writer that restarted could carry out a page hand-off meant for its previous run, losing index entries.
AWS fault testA writer asked for a page it had just received could send an older copy while still adding rows to it.
Randomized runA writer reading a page back from storage could send the older copy while also changing it.
AWS randomized runA request carried out after its sender had given up could drop a page in the middle of a change.
Code reviewA writer killed while waiting for the log servers could still hand over a page whose changes were then lost, leaving an index entry for a row that never existed.
AWS randomized runA commit cancelled while it waited for the log servers became visible to the other writers before it was durable, so a crash could leave them keeping a change its own writer had lost.
Code readingA writer rejoining while another was growing a table could stall both until a timeout.
Randomized runAFTER UPDATE triggers and foreign-key checks saw the new row as the old one, so orphan rows were possible.
Regression suiteAn evicted writer could answer a query from stale data before it learned it was out. Writers now check they still hold a read lease before each snapshot.
Fault test, one machineNew hash index pages skipped the step that tells the other writers who holds the latest copy, so pages could be lost.
Feature testAn UPDATE that moved a row to another partition made a concurrent UPDATE or DELETE of that row silently do nothing.
Isolation suiteAn UPDATE or MERGE that waited for another transaction could write back a value that transaction rolled back.
Isolation suiteA foreign-key check at REPEATABLE READ didn't fail after a concurrent change to the parent row, as PostgreSQL's does.
Isolation suiteAn ALTER TABLE ... TYPE that kept an index's file deleted that file on the other writers.
Regression suiteWriters restarted in place did not follow the storage when it moved to another page server.
AWS storage testA split of the page version map could stall the service that hands pages between writers.
Stress testA management port that takes no password listened on every network interface instead of only on the machine itself.
Code review
The most serious bugs found so far, all fixed. Since October 29 our tests and reviews have found more than 30 bugs in all, and every one is fixed.
Roadmap
Three stages, each a product on its own
Customers who need RAC's availability before its scale-out can start with the first two stages. The third, several writers on the same tables, is working on AWS with three writers.
-
Stage 1
One writer, many readers
Automatic failover and read scale-out on shared storage.
Tested on AWSNeon's storage handles the failover; our control plane automates it.
-
Stage 2
A writer per database
Many databases on one storage service, each with its own writer, moved between servers without copying data.
Tested on AWSNeon's storage supports it; our control plane places and moves the writers.
-
Stage 3
Writers share tables
Several servers writing the same tables at once, the way Oracle RAC does.
Working on AWS with 3 writersOur own work: up to eight writers on every table, membership without a coordinator, schema changes on all writers.
What production needs next
Throughput on shared tables. Speed when every writer changes the same tables has not been tuned yet.
NextEncrypted traffic between writers and between writers and storage, with the option of a customer's own KMS key.
NextRead-ahead for shared tables, so cold full scans are no longer limited to about 200 pages a second per writer.
NextDisaster recovery to another region. Built on October 30; the local test brought a cluster back in 10 s. The AWS rerun is still to do.
In testMulti-terabyte tests on bigger machines.
PlannedRolling upgrades, one writer at a time.
PlannedSeveral page servers. A standby for every database with automatic failover, and shards spread across page servers while the database runs.
Tested on AWSOctober 30 features in the installer. Several databases per cluster, SET LOGGED / UNLOGGED, large rows and synod_cdc; the AWS suite passed on that build.
Done
Funding and design partners
Help take Synod from tested prototype to production
We are raising to hire experienced database engineers and turn Synod into something teams can run in production: multi-writer clusters, failover, upgrades, backups and monitoring.
Design partners
We're looking for a few teams that run Oracle RAC today, or run PostgreSQL and can't afford a gap when a server fails. Migration firms with RAC databases their clients couldn't move are welcome too.
What a design partner gets
- A free pilot on a non-production copy, installed with our installer and pilot operations guide
- Direct engineering support from the team that built Synod
- Founding-customer pricing when Synod goes to production
What we ask
- An hour of feedback every two weeks during the pilot
- A realistic workload, so the tests reflect what you run
Investors
We're raising a seed round. Write to us for the deck and a walkthrough of the fault tests.
contact@synoddb.com
Tell us what you run today: the database, how many servers, and what a failure costs you.