TinyStore

Beyond one machine / When the home dies

When the home dies

A file's home is the one machine that writes it. While it runs, its copies read and pass their writes to it. This chapter covers what happens when it stops: moving a home on purpose, promoting a copy by hand when the home is gone, and the epochs that keep a file from ever having two writers.

Why nothing happens by itself

No node can tell a dead home from a cut cable: both are silence. A copy that guessed wrong and started writing while the old home lived would make a second home, and the file would split into two histories for good. So in the first version a person decides, once they know the old home is stopped.

Epochs

Every change of a file's home raises the file's epoch. A node refuses pages of an older epoch, and a home refuses writes once a later epoch reaches it (contract REPL-08). An old home that wakes up after its copy was promoted cannot write over the new history: the first link it makes tells it that it is no longer the home.

Move a home that is alive

rust
tinystore::replication::move_home(&store, "sql/app.db")?; // on the node that takes the file over

The old home stops taking writes, ships its last commits, and the new home opens its writer at the next epoch, through the catalog. No commit is lost (contract REPL-10). Until the new home's claim reaches the catalog, the old home can call the move off; after that it never can.

Promote a copy when the home is gone

America's data centre is down and will not come back soon. Europe holds the freshest copy:

sh
# stop Europe's server first
tinystore node promote ./eu --from us.internal:7444
tinystore serve ./eu --node-listen 0.0.0.0:7444 --node-config node.toml   # without --from: it is the home now

promote asks each node named by --from first. If America still answers and serves the file, promote refuses, because promoting then would make a second home; --force overrides that, for a person who knows better. The promoted files continue at the next epoch, and nodes that copied from America try the next address in their from and find Europe.

await sessions.under(userId).set(digest(token), session)
  1. America commits 499 and 500, and goes down before their pages reach Europe.
  2. A person checks that America is stopped, and runs node promote on Europe. Europe becomes the home at the next epoch.
  3. Writes go on in Europe. Commits 499 and 500 were never shipped: they are lost, and the promotion says so.
  4. America comes back. Its epoch is older, so it takes no writes, and node demote makes its files copies of Europe.

By default a home answers a write after its own commit, so the commits that had not reached a copy when the home died are lost with it. That is the trade-off of asynchronous copies. A synchronous home, from Readers in another region, loses nothing it answered: every success means a copy held the write (contract REPL-09).

Bring the old home back

sh
tinystore node demote ./us
tinystore serve ./us --from eu.internal:7444 --node-listen 0.0.0.0:7444 --node-config node.toml

demote makes the old home's files copies again. The commits it had not shipped are dropped, counted and logged, and it follows the new home before it opens any writer.

Why this design?

Why a person and not automatic failover. Automatic failover is only safe with something outside the two nodes that decides, and that keeps the decision durably: a judge. Without one, a network cut that hides the home from its copy looks exactly like a dead home. TinyStore's witness is that judge, and it is a draft. Until it is promised, the safe default is a command that a person runs, and that refuses when the old home still answers.

Why epochs. A promotion is a decision that must win over everything the old home does afterwards, including writes it accepted while it was cut off. A number that only grows, carried by every page and every write, makes the older side lose every time, without asking anyone.

Why the loss is counted and logged, not hidden. A failover that loses commits silently leaves the program believing writes that no longer exist. TinyStore names how many were lost, and offers the synchronous mode for files where none may be.

See also

Edit this page on GitHub Updated 19 October 2026

esc
↑↓ navigate ↵ open ⇥ filter 0 results