fix(db): provision CV builder + AI workspace tables via the MySQL-safe reconciler
Deploy failed on prod (MariaDB) while the test job was green: backend startup threw during Database.Migrate(), so deploy.sh's post-deploy health check exited. Root cause: AddCvVariants/AddAiInteractions were scaffolded against SQLite, so they bake SQLite type names into their DDL — DateTimeOffset emits `TEXT`, bool/int emit `INTEGER`, and the PK gets no AUTO_INCREMENT. Run against MariaDB that yields a structurally wrong table, and the composite index over a TEXT column then trips "ERROR 1071: Specified key was too long; max key length is 3072 bytes". SQLite accepts all of it, which is why local/container verification passed. Fix, following the pattern already used for CareerProfiles/AiWorkspaceNotes: - both migrations become no-ops; the three tables are reconciler-owned - reconciler provisions them idempotently per dialect (MySQL: varchar/int AUTO_INCREMENT/datetime(6); SQLite: CREATE TABLE IF NOT EXISTS) - DropMalformedMySqlTable rebuilds a half-built table left by the failed migration, guarded on row count so a table with ANY rows is never dropped - bound the indexed string columns with HasMaxLength so the model matches Verified against a real MariaDB 11 container: reproduced error 1071, then confirmed the corrected DDL yields auto_increment PKs, varchar/datetime columns and all previously-failing indexes. 306 backend tests green; SQLite container starts clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,51 +8,18 @@ namespace JobTrackerApi.Migrations
|
||||
/// <inheritdoc />
|
||||
public partial class AddAiInteractions : Migration
|
||||
{
|
||||
// Intentionally a no-op — AiInteractions is provisioned by the reconciler in
|
||||
// StartupInitializationExtensions (idempotent, MySQL-safe: Module/OwnerUserId are varchar so
|
||||
// MariaDB can index them). The original CREATE INDEX here indexed unbounded string columns,
|
||||
// which crashes backend startup on MariaDB. See the AddCvVariants migration for the full note.
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AiInteractions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
OwnerUserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
JobApplicationId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Module = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Mode = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Title = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Provider = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ResultJson = table.Column<string>(type: "TEXT", nullable: false),
|
||||
CreatedAtUtc = table.Column<DateTimeOffset>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AiInteractions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AiInteractions_JobApplications_JobApplicationId",
|
||||
column: x => x.JobApplicationId,
|
||||
principalTable: "JobApplications",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AiInteractions_JobApplicationId",
|
||||
table: "AiInteractions",
|
||||
column: "JobApplicationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AiInteractions_OwnerUserId_JobApplicationId_Module_CreatedAtUtc",
|
||||
table: "AiInteractions",
|
||||
columns: new[] { "OwnerUserId", "JobApplicationId", "Module", "CreatedAtUtc" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AiInteractions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user