7f426e255c
A completely empty MariaDB database could not start: the reconciler assumed migration-owned tables already existed, and migrations assumed reconciler-owned tables already existed. Neither could go first. Existing databases worked, so only fresh installs were affected. Startup is now an explicit sequence: connect, reconcile, migrate, reconcile, start. The reconciler runs twice because neither position alone works — pass 1 repairs legacy schemas and creates the reconciler-owned tables that migrations reference, pass 2 picks up everything that could not exist yet on a fresh database. Every statement is existence-guarded, so the second pass is a no-op scan on a correct database. Untangled the overlapping ownership: - RuleSettings is migration-owned. The reconciler also created it, which made a clean install fail with "Table 'RuleSettings' already exists". It now only seeds the default row, and only once the table exists. - The six CareerProfile child tables are reconciler-owned. Their migration was scaffolded against SQLite and indexed an unbounded longtext OwnerUserId, which exceeds MariaDB's 3072-byte key limit; it is now a no-op and the reconciler carries correct per-provider DDL. OwnerUserId and ItemKey are bounded to varchar(255) in the model so the index fits. - Reconciler tables that reference another table are guarded on their parent, so pass 1 skips them on an empty database instead of failing on the foreign key. - All index creation goes through one EnsureMySqlIndex helper, guarded on table existence as well as index existence. This removes ten copies of the raw block that crashed on a missing table. - The DbContext-owned connection is no longer disposed by the reconciler, and Open() is guarded on connection state, so the second pass can reuse it. Verified against MariaDB 11 and SQLite: empty MariaDB (40 tables, starts), restart on the populated database (idempotent, rows preserved), empty MariaDB via the Docker image, fresh SQLite (42 tables), and an existing partially migrated SQLite dev database (34 tables upgraded to 44 with all 13 applications and 8 companies intact). 329 backend tests pass in Release. Ownership rules, startup order, fresh install and production upgrade are documented in docs/infrastructure/database-ownership.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
29 lines
1.1 KiB
C#
29 lines
1.1 KiB
C#
using Microsoft.EntityFrameworkCore.Migrations;
|
|
|
|
#nullable disable
|
|
|
|
namespace JobTrackerApi.Migrations
|
|
{
|
|
/// <inheritdoc />
|
|
public partial class AddCareerProfileRelationalChildren : Migration
|
|
{
|
|
// Deliberately a no-op. Scaffolded against SQLite, so on MariaDB it emitted an unbounded
|
|
// longtext OwnerUserId and then indexed (OwnerUserId, CareerProfileId, SortOrder) over it,
|
|
// which exceeds MySQL's 3072-byte key limit — a clean MariaDB install died here.
|
|
//
|
|
// The six CareerProfile child tables are provisioned by the idempotent reconciler in
|
|
// StartupInitializationExtensions, which carries correct DDL per provider. Existing databases
|
|
// already have this migration recorded as applied, so emptying it changes nothing for them.
|
|
// docs/infrastructure/database-ownership.md.
|
|
/// <inheritdoc />
|
|
protected override void Up(MigrationBuilder migrationBuilder)
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void Down(MigrationBuilder migrationBuilder)
|
|
{
|
|
}
|
|
}
|
|
}
|