GrainFed

Migrating from Pixelfed

There is no data migration, because there is no data to migrate. GrainFed opens the database Pixelfed was already using. What follows is a sequence of checks around one small, deliberate moment of downtime.

What does not change

The five phases

Everything is dry-run until you pass --apply. Everything before cutover is reversible, and cutover is deliberately the smallest step — all the expensive work happens before it.

PhaseWhat it doesWrites?
inspect Runs every check and reports a verdict: whether this instance can be adopted, and what would stop it. Nothing at all
prepare Creates GrainFed's gf_* tables and records a baseline of exactly what the instance looked like beforehand. Its own tables only
cutover Stops Pixelfed, verifies it is quiet, starts GrainFed. The point of no return. Yes
verify Checks that federation, tokens and media still work afterwards. Nothing
revert Puts it back. Yes
grainfed adopt --phase inspect
grainfed adopt --phase prepare --apply
grainfed adopt --phase cutover --apply
grainfed adopt --phase verify

Take a database backup before cutover. Adoption is designed to be reversible and the baseline exists so that revert can be exact, but a backup is the thing that is true regardless of whether this software is correct.

Giving it back

Reverting is a supported operation and not an emergency procedure:

grainfed adopt --phase revert --apply

It stops GrainFed, restores what the baseline recorded, and starts Pixelfed again. Because no Pixelfed table was ever altered, Pixelfed finds the database it expects. Posts made while GrainFed was running are ordinary rows in the ordinary tables, so they are still there.

If you want the gf_* tables gone too, --revert --purge drops exactly the ones adoption created, by name, and leaves alone any that predate it.

Then make it faster

Adoption deliberately leaves the schema exactly as Pixelfed had it. That is what makes dropping GrainFed in — and taking it back out — possible. Optimisation is a separate step you run when you are ready, and can re-run later as the project learns about more:

grainfed optimize            # report; changes nothing
grainfed optimize --apply    # make the changes
grainfed optimize --revert   # put them back exactly

It only ever changes indexes — never a column, a row or a table. An index changes how fast a query is answered and never what it answers, so an instance that never runs this is correct and merely slower, and Pixelfed keeps working against a schema that has run it. Every change is recorded before it is made, together with the statement that undoes it.

On the first instance it ran against, it found 37 indexes that could never be chosen — each one a leading prefix of another index on the same table, written on every insert for nothing — and two worth adding.

Things worth knowing first

Be honest with yourself about the stage. GrainFed serves every route Pixelfed can serve and is verified against a real database, a real browser and real remote servers — but it runs one production instance. If you are moving a community that would be upset by a bad week, wait, or run it beside your real instance first.