Repository navigation
Upgrade safety: migration timeout, retry-safe migrations, deploy settings docs #384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bebff8d
d381a55
f4aed74
ea9b6a1
5c9b884
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| using System.Threading.Tasks; | ||
| using Microsoft.EntityFrameworkCore; | ||
| using Microsoft.EntityFrameworkCore.Infrastructure; | ||
| using Microsoft.EntityFrameworkCore.Migrations; | ||
| using Npgsql; | ||
| using SW.PrimitiveTypes; | ||
| using Testcontainers.PostgreSql; | ||
| using Xunit; | ||
|
|
||
| namespace SW.Bitween.IntegrationTests.Tests; | ||
|
|
||
| /// <summary> | ||
| /// A database whose promoted values index was dropped by hand - the way to stop long promoted values | ||
| /// failing to save before DropPromotedValuesIndex dropped it for everyone. That migration assumed the | ||
| /// index was there, so on such a database it failed and the app crash-looped on startup. | ||
| /// </summary> | ||
| public sealed class HandDroppedIndexMigrationTests : IAsyncLifetime | ||
| { | ||
| private readonly PostgreSqlContainer _postgres = new PostgreSqlBuilder().Build(); | ||
|
|
||
| public Task InitializeAsync() => _postgres.StartAsync(); | ||
|
|
||
| public async Task DisposeAsync() => await _postgres.DisposeAsync(); | ||
|
|
||
| [Fact] | ||
| public async Task A_database_without_the_promoted_values_index_migrates_to_the_latest() | ||
| { | ||
| await using var db = CreateDbContext(); | ||
| await db.GetService<IMigrator>().MigrateAsync("20260930150916_ExchangeRetention"); | ||
| await db.Database.ExecuteSqlRawAsync("DROP INDEX infolink.ix_xchange_promoted_properties_properties_raw;"); | ||
|
|
||
| await db.Database.MigrateAsync(); | ||
|
|
||
| Assert.Empty(await db.Database.GetPendingMigrationsAsync()); | ||
| } | ||
|
|
||
| private PgSql.BitweenDbContext CreateDbContext() | ||
| { | ||
| var dataSource = new NpgsqlDataSourceBuilder(_postgres.GetConnectionString()); | ||
| dataSource.EnableDynamicJson(); | ||
| var options = new DbContextOptionsBuilder<PgSql.BitweenDbContext>() | ||
| .UseSnakeCaseNamingConvention() | ||
| .UseNpgsql(dataSource.Build(), b => | ||
| { | ||
| b.MigrationsHistoryTable("_ef_migrations_history", PgSql.BitweenDbContext.Schema); | ||
| b.MigrationsAssembly(typeof(PgSql.DbType).Assembly.FullName); | ||
| }) | ||
| .Options; | ||
|
|
||
| return new PgSql.BitweenDbContext(options, new RequestContext(), new NoPublish()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,8 +23,9 @@ service: | |
| probes: | ||
| # Startup /health/live, liveness /health/live, readiness /health/ready (database and RabbitMQ). | ||
| enabled: true | ||
| # Five-second checks: 60 gives a starting pod five minutes, for migrations, before it is restarted. | ||
| startupFailureThreshold: 60 | ||
| # Five-second checks: 480 gives a starting pod 40 minutes, for migrations, before it is restarted. | ||
| # Longer than a migration statement may run (30 minutes), so one that runs out logs why. | ||
| startupFailureThreshold: 480 | ||
|
Comment on lines
+26
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
sed -n 55,85p charts/default/templates/deployment.yamlRepository: simplify9/Bitween-api Length of output: 1337 Do not use 480 as the generic startup threshold. The startup probe checks 🤖 Prompt for AI AgentsSources: Path instructions, Learnings
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping 480. The startup probe calls |
||
|
|
||
| # North-south routing. The chart uses ingress-nginx by default. Set | ||
| # gateway.enabled=true to switch to Gateway API (HTTPRoute) instead - this | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: simplify9/Bitween-api
Length of output: 500
🏁 Script executed:
Repository: simplify9/Bitween-api
Length of output: 45673
🏁 Script executed:
Repository: simplify9/Bitween-api
Length of output: 45675
🏁 Script executed:
Repository: simplify9/Bitween-api
Length of output: 31808
Enable MySqlConnector user variables for this migration.
DropPromotedValuesIndex.UpusesSET @drop = .... The production MySQL context uses Pomelo's MySqlConnector provider, andAllowUserVariablesdefaults tofalse. WithoutAllowUserVariables=true, MySqlConnector can treat@dropas an unset parameter and fail beforePREPAREruns. The multi-statement batch itself is allowed by default.Set
AllowUserVariables=truein the MySQL connection-string contract, or rewrite the migration to avoid a user variable. Add a MySQL integration test that uses the production connection settings and starts with the index already absent.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked on MySQL 8.0 through Pomelo 9.0.0 with a connection string that doesn't set
AllowUserVariables. With the index dropped by hand, all 92 migrations applied, including this one. On a fresh database, where the index exists, it was dropped and all 92 applied. Pomelo setsAllowUserVariables=trueon the connections it opens, so@dropworks. No change.