Private
Public Access
1
0

Make SimpleFIN split migration run only once

Track completion in a `data_migrations` table. On startup the
migration checks if it has already been applied and skips the
SimpleFIN API calls entirely, avoiding the slow startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-09 00:03:10 -05:00
parent 8ca2dbd763
commit 10bbca84be
@@ -10,13 +10,28 @@ namespace Purrse.Api.Services;
/// <summary>
/// One-time data migration that splits SimpleFIN connections containing accounts
/// from multiple institutions into separate connections per institution.
/// Tracks completion in a `data_migrations` table so it only runs once.
/// </summary>
public static class SimpleFinConnectionMigration
{
private const string MigrationName = "SplitSimpleFinConnectionsByInstitution";
public static async Task RunAsync(IServiceProvider services, ILogger logger)
{
using var scope = services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<PurrseDbContext>();
// Create tracking table if it doesn't exist, then check if already applied
await db.Database.ExecuteSqlRawAsync(
"CREATE TABLE IF NOT EXISTS data_migrations (name TEXT PRIMARY KEY, applied_at TIMESTAMP NOT NULL)");
var alreadyApplied = await db.Database
.SqlQueryRaw<int>($"SELECT 1 AS \"Value\" FROM data_migrations WHERE name = '{MigrationName}'")
.AnyAsync();
if (alreadyApplied)
return;
var encryption = scope.ServiceProvider.GetRequiredService<IEncryptionService>();
var simpleFin = scope.ServiceProvider.GetRequiredService<SimpleFinSyncProvider>();
@@ -26,7 +41,10 @@ public static class SimpleFinConnectionMigration
.ToListAsync();
if (connections.Count == 0)
{
await MarkCompleteAsync(db);
return;
}
var splitCount = 0;
@@ -113,5 +131,13 @@ public static class SimpleFinConnectionMigration
await db.SaveChangesAsync();
logger.LogInformation("SimpleFIN connection migration complete: created {Count} new connections", splitCount);
}
await MarkCompleteAsync(db);
}
private static async Task MarkCompleteAsync(PurrseDbContext db)
{
await db.Database.ExecuteSqlRawAsync(
$"INSERT INTO data_migrations (name, applied_at) VALUES ('{MigrationName}', NOW()) ON CONFLICT DO NOTHING");
}
}