migration:up keeps a per-version run cache so a migration whose code is unchanged since its last successful apply is skipped without touching the database. The database migrations table stays the source of truth for what has been applied; the cache is only a fast path. When every registered migration is already recorded and unchanged, the runner skips opening a database connection altogether and prints an up to date (cached) line for each.
What the cache stores
One JSON file per applied version lives undervar/cache/migrations/ at the project root, so editing a single migration invalidates only that migration’s entry. Each entry records the cache version, the migration id, a fingerprint hash, and the time it was applied. Only successful applies are written, and the directory is safe to delete at any time.
How an entry is fingerprinted
The hash covers the migration’s version, the target table name, the target database URL, and the source of itsup() and down() methods. Any change to the migration’s code — or pointing at a different database or table — yields a new hash and therefore a cache miss, so the migration is re-checked against the tracking table and run if pending.
When the cache is bypassed
| Trigger | Effect |
|---|---|
--drop | The database is reset, so a cache hit would wrongly skip the rebuild; the cache is bypassed and every migration re-runs. |
--no-cache | Explicit escape hatch — reads and writes are skipped for the run. |
migration:down | A rollback deletes the rolled-back version’s cache entry, so the next migration:up re-applies it instead of treating it as up to date. |
--drop and --no-cache exist for exactly that case.
Next steps
- Running migrations: apply migrations, with or without the cache.
- Rolling back: how a rollback invalidates the cache entry it reverses.