How-to guide

Run migrations outside startup

By default UsePostgres and UseSqlite migrate the schema while the host registers its services. Move that out of the host when:

  • it is a Lambda runner or another serverless function, where every cold start would open a connection and take the migration lock even when nothing is pending;
  • it connects to a read replica, which must never be migrated;
  • a deployment step already migrates the database before the new version starts.

1. Skip the migration in the host

Call SkipMigrations() before the data provider:

using Trax.Effect.Data.Postgres.Extensions;
using Trax.Effect.Extensions;
 
var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .SkipMigrations()
        .UsePostgres(builder.Configuration.GetConnectionString("Trax")!)
    )
);
 
builder.Build().Run();

Called after UsePostgres, it changes nothing: the migration has already run.

2. Migrate from the deployment

Run DatabaseMigrator.Migrate once per deployment, before any host of the new version starts serving. A small console project referencing Trax.Effect.Data.Postgres is enough:

using Trax.Effect.Data.Postgres.Utils;
 
var connectionString =
    Environment.GetEnvironmentVariable("TRAX_CONNECTION_STRING")
    ?? throw new InvalidOperationException("Set TRAX_CONNECTION_STRING.");
 
await DatabaseMigrator.Migrate(connectionString);

It is safe to run from several places at once: the migrator holds an advisory lock for the whole run, so a second caller waits and then finds nothing pending. For SQLite, Trax.Effect.Data.Sqlite.Utils.DatabaseMigrator.Migrate does the same for a database file.

3. Keep one host migrating, if you want

Hosts that do not skip still migrate on startup. A common split is an API or scheduler host that migrates and Lambda runners that skip, so the schema moves forward when the long-running host deploys.

See also

SDK Reference

SkipMigrations | DatabaseMigrator | UsePostgres