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:
@@ -298,6 +298,12 @@ namespace JobTrackerApi.Data
|
||||
// owns career data. PublicSlug is globally unique so /cv/{slug} can resolve it anonymously.
|
||||
modelBuilder.Entity<CvVariant>()
|
||||
.HasQueryFilter(x => CurrentUserId != null && x.OwnerUserId == CurrentUserId);
|
||||
// Bound the indexed string columns so Pomelo maps them to varchar, not longtext (MariaDB
|
||||
// cannot index a TEXT/longtext column without a prefix length). Actual prod DDL is applied by
|
||||
// the reconciler (StartupInitializationExtensions), MySQL-safe; see that file + DbContext note.
|
||||
modelBuilder.Entity<CvVariant>().Property(x => x.OwnerUserId).HasMaxLength(255);
|
||||
modelBuilder.Entity<CvVariant>().Property(x => x.PublicSlug).HasMaxLength(64);
|
||||
modelBuilder.Entity<CvVariantVersion>().Property(x => x.OwnerUserId).HasMaxLength(255);
|
||||
modelBuilder.Entity<CvVariant>()
|
||||
.HasIndex(x => x.PublicSlug)
|
||||
.IsUnique();
|
||||
@@ -324,6 +330,9 @@ namespace JobTrackerApi.Data
|
||||
// docs/architecture/ai-career-assistant.md.
|
||||
modelBuilder.Entity<AiInteraction>()
|
||||
.HasQueryFilter(x => CurrentUserId != null && x.OwnerUserId == CurrentUserId);
|
||||
// varchar (not longtext) for the indexed columns — see the CvVariant note above.
|
||||
modelBuilder.Entity<AiInteraction>().Property(x => x.OwnerUserId).HasMaxLength(255);
|
||||
modelBuilder.Entity<AiInteraction>().Property(x => x.Module).HasMaxLength(64);
|
||||
modelBuilder.Entity<AiInteraction>()
|
||||
.HasIndex(x => new { x.OwnerUserId, x.JobApplicationId, x.Module, x.CreatedAtUtc });
|
||||
modelBuilder.Entity<AiInteraction>()
|
||||
|
||||
Reference in New Issue
Block a user