Migrations
Apply the schema changes a directory of migration files describes, in order, once each, and record which ones have run.
Two things make that safe to run at boot on more than one node. Every applied migration is written to a table, so a second start does not run it again. And the run takes a lock in the database first, so two nodes starting together do not both apply the same change to the same schema; the second waits and then finds there is nothing left to do.
A lock whose holder died would otherwise stop every later boot, so one older than staleAfterMs is taken over.
File names are checked against a fixed pattern, so what runs is the ordered set the directory declares rather than whatever is sitting there.
b.migrations.create(opts) #
{
dir: string, // directory holding the migration files; required
db: object, // database handle; defaults to b.db
staleAfterMs: number, // when a held lock may be taken over
}
Open the runner over a directory and answer the handle to work it. The handle carries up(), which applies every migration not yet recorded; down(), which reverses the last one applied; status(), which answers what has run and what has not; and dir.
A file is a migration when its name matches , and the number orders the run. Anything else in the directory is skipped, so an editor backup or a note does not become a schema change.
up() takes a row lock in the database before it reads the applied set, so two nodes booting together do not apply the same file twice: the second waits, then finds nothing outstanding. A lock left behind by a process that died is taken over once it is older than staleAfterMs, since otherwise one crash would stop every later boot.
var b = require("@blamejs/core");
var m = b.migrations.create({ dir: "./migrations" });
m.status(); // → what has run and what has not
m.up(); // → applies 001-create-users.js, then 002-add-index.js