Data migrations, kept apart from schema changes and run in batches.
Filling a new column on a table with millions of rows inside a migration holds locks for minutes and blocks the deploy. A backfill runs in batches instead: inline in the migration for small tables, or deferred to a process in the running app after the deploy, recording its progress after every batch so it resumes where it stopped after a crash or a restart.
defmodule MyLib.Backfills.Slugs do
use PhoenixMountable.Backfill,
table: "my_lib_items",
where: "slug IS NULL",
set: "slug = LOWER(title)",
batch_size: 1_000
end
# in a migration version
def up(ctx) do
alter table(:my_lib_items, prefix: ctx.prefix), do: add(:slug, :string)
backfill(ctx, MyLib.Backfills.Slugs)
endA later contract version that relies on the data declares
requires_backfills: [MyLib.Backfills.Slugs], and refuses to run until the
backfill has finished.
Options
:mode—:inline(the default) runs every batch inside the migration;:deferredrecords the backfill as pending, to be run byPhoenixMountable.Backfill.Runner(ormix phoenix_mountable.backfill). The host overrides it for a migration withup(version: n, backfills: :deferred).:batch_size— rows per batch. Defaults to1_000.:throttle— milliseconds to wait between batches (deferred mode). Defaults to0.
For the common case, an UPDATE keyed by an ordered column:
:table— the table.:key— the column to paginate by. Defaults to"id".:where— an SQL condition selecting the rows still to fill.:set— the SQL assignments.
For anything else, implement batch/3 instead.
Summary
Callbacks
Processes one batch after cursor (nil for the first), returning the
cursor to continue from and the number of rows processed, or :done.
Callbacks
@callback batch( PhoenixMountable.Migrations.Context.t(), cursor :: term(), batch_size :: pos_integer() ) :: {:cont, term(), non_neg_integer()} | :done
Processes one batch after cursor (nil for the first), returning the
cursor to continue from and the number of rows processed, or :done.
Runs in a transaction together with the recording of the new cursor, so a batch is either fully done and recorded, or not at all.