Before the Scary Migration
Every developer has run a migration they were not sure about and felt the small cold moment afterwards. This chapter removes it, in two commands, and explains why this particular feature could only come from the thing that installed your database.
The problem
A destructive migration cannot be undone by
migrate:rollback. The down()
method restores the schema; it cannot restore the
column you dropped, because the data went with it. And in
local development the data is often worth more than it
sounds: three weeks of hand-made test cases covering the
edge conditions you had to reason about, which a fresh
seed does not contain.
So people do one of two things. They avoid the experiment — writing the migration defensively, never trying the cleaner schema — or they run it and lose an afternoon rebuilding fixtures. Both are the same tax, paid differently.
The hard way
You can do this yourself:
mysqldump to a file, remember where you put
it, remember the flags, restore with the inverse command,
remember which of the four dumps in your Downloads folder
was the right one. It works, and the number of people who
actually do it before every risky migration is
approximately zero, because the ceremony is longer than the
migration.
Two commands
grove db snapshot --db myapp --note "before migrate"
# ...run the scary migration...
grove db list
grove db restore <id>
Data restored exactly as it was. The note is not
decoration — a week later, grove db list
with five snapshots in it is unreadable without one.
“Exactly” has been true since 1.9.0, and it is worth knowing why it was not before. A MySQL dump recreates the tables it holds and says nothing about any others, so a restore after a migration that created a table left that table in place — the one case a snapshot before a migration exists for. Each database in the dump is now dropped and recreated on the way in. If you are on an older Grove, upgrade before you rely on this chapter.
Snapshots live under Grove's own directory rather than in your project, which keeps a database dump out of your repository and out of your build context. MySQL and PostgreSQL snapshots are plain SQL dumps — readable, greppable, restorable by hand if you ever want to. An ElyraSQL snapshot is a hot, consistent copy of its single database file, taken while it serves.
A database per branch
A snapshot answers “put it back”. There is a
second, quieter version of the same problem: you switch
from a feature branch to main, and the feature
branch's migrations are still in the database. Now
main is running against a schema it has never
seen, and the errors look like bugs in main.
grove db branches on
Since 1.9.0 a project can have a database per git branch.
Check out a branch and its data is swapped in; go back to
main and main's data comes
back exactly as it was left. The first checkout of a new
branch starts it from the data you were on.
The design decision worth noticing: the database
keeps its name, and only its contents move. So the
app, php artisan in a terminal, a test run and
a database client all see the checked-out branch without
being told. Pointing the app at a different database per
branch would have reached only the requests Grove proxies,
and your terminal would have kept migrating the old one. On
MySQL the swap is a single atomic RENAME TABLE,
measured at 43–46 ms for a 70 MB schema.
Why only a supervisor can do this
This is the chapter that makes the argument for the whole course, so it is worth stating directly.
A tool that merely connects to your database can
dump it. Only a tool that owns it knows which
engine is running, on which port, with which credentials,
for which project — without being told, every time,
by you. Grove installed the database, so
grove db snapshot needs no connection string:
it already knows.
That is the same property that made grove env
correct in chapter 6 and per-site PHP routing possible in
chapter 5. Each is small on its own. Together they are the
difference between a collection of tools and an
environment.
Make it a habit
Snapshot before a migration you have not run before.
Snapshot before running someone else's migration on
your data. Snapshot before migrate:fresh --seed
on a database you have been building up by hand.
It costs seconds and it changes what you are willing to try — which is the real benefit. The best version of a schema is usually the third one you attempt, and you only get to the third if the first two were cheap.
What you learned
- down() restores the schema, not the data. Hand-made local fixtures are worth more than they sound.
- Two commands, with a note, because five unlabelled snapshots are as good as none.
- Snapshots live in Grove's directory, not in your repository or your build context.
- Owning the database is what makes it one command — the same property behind grove env and per-site PHP.
- Cheap experiments change what you attempt. The best schema is usually the third one.