From 10bbca84be39ca2531bcbf9259ccc14330018fff Mon Sep 17 00:00:00 2001 From: Catherine Renelle <32781029+catrenelle@users.noreply.github.com> Date: Mon, 9 Feb 2026 00:03:10 -0500 Subject: [PATCH] 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 --- .../Services/SimpleFinConnectionMigration.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Purrse.Api/Services/SimpleFinConnectionMigration.cs b/src/Purrse.Api/Services/SimpleFinConnectionMigration.cs index f9d03ce..fd52edc 100644 --- a/src/Purrse.Api/Services/SimpleFinConnectionMigration.cs +++ b/src/Purrse.Api/Services/SimpleFinConnectionMigration.cs @@ -10,13 +10,28 @@ namespace Purrse.Api.Services; /// /// 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. /// 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(); + + // 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($"SELECT 1 AS \"Value\" FROM data_migrations WHERE name = '{MigrationName}'") + .AnyAsync(); + + if (alreadyApplied) + return; + var encryption = scope.ServiceProvider.GetRequiredService(); var simpleFin = scope.ServiceProvider.GetRequiredService(); @@ -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"); } }