Pin the autovacuum dialect guard with a test
The guard itself is already on main. The test that would have caught its
absence is not, so nothing stops the same mistake being made again.
TuneUserDataAutovacuum emits a PL/pgSQL DO $$ block. SQLite also conforms
to SQLDatabase, so the original guard -- a plain `as? any SQLDatabase` --
passed on SQLite and fed it a block it cannot parse. Fluent turns a failed
migration into a process exit at boot, so every SQLite-backed relay in the
fleet crash-looped while the Postgres one beside them stayed healthy and
the deploy gate reported the fleet OK.
Four cases, each pinning a different part of the failure:
* the whole boot schema migrates cleanly on SQLite, which is the shape
that actually broke
* the migration applied alone is a no-op rather than an error, so a
failure names this migration instead of whichever runs first
* the SQLite dialect still reports the literal the guard compares
against, because a rename would silently skip Postgres too and the
tuning would then never be applied anywhere
* the migration is still registered in the boot list, since a
Postgres-only migration has to stay safe to register everywhere
Falsified rather than assumed. Against main as it stands the four pass.
With the dialect check removed from the guard the run does not pass, it
dies with an assertion, which is the same class of failure the fleet hit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Paul Hitt
co-authored by
Claude Opus 5
parent
b38480bf91
commit
9d0708229c
@@ -0,0 +1,59 @@
|
||||
import Fluent
|
||||
import FluentSQLiteDriver
|
||||
import Foundation
|
||||
import SQLKit
|
||||
import Testing
|
||||
import Vapor
|
||||
|
||||
@testable import FlamenetRelay
|
||||
|
||||
/// `TuneUserDataAutovacuum` writes a PL/pgSQL `DO $$` block, which only
|
||||
/// Postgres parses. SQLite also conforms to `SQLDatabase`, so a guard that
|
||||
/// only checks the protocol lets the block through and SQLite rejects it with
|
||||
/// `near "DO": syntax error`. Fluent treats that as a failed migration, the
|
||||
/// process exits at top level, and the relay cannot boot.
|
||||
@Suite("autovacuum tuning is Postgres only")
|
||||
struct AutovacuumDialectTests {
|
||||
|
||||
/// Booting the whole schema on SQLite is what the relay does on startup
|
||||
/// and what the test harness does, so this is the shape that broke.
|
||||
@Test("the boot schema migrates cleanly on SQLite")
|
||||
func bootSchemaOnSQLite() async throws {
|
||||
let app = try await Application.make(.testing)
|
||||
app.databases.use(.sqlite(.memory), as: .sqlite)
|
||||
for m in RelayMigrations.all() { app.migrations.add(m) }
|
||||
try await app.autoMigrate()
|
||||
try await app.asyncShutdown()
|
||||
}
|
||||
|
||||
/// Applied on its own, so a failure names this migration rather than
|
||||
/// whichever one happens to run first.
|
||||
@Test("the migration is a no-op on SQLite, not an error")
|
||||
func tuningAloneIsSkippedOnSQLite() async throws {
|
||||
let app = try await Application.make(.testing)
|
||||
app.databases.use(.sqlite(.memory), as: .sqlite)
|
||||
let tuning = TuneUserDataAutovacuum()
|
||||
try await tuning.prepare(on: app.db)
|
||||
try await tuning.revert(on: app.db)
|
||||
try await app.asyncShutdown()
|
||||
}
|
||||
|
||||
/// The guard keys off the dialect name, so pin the two literals it
|
||||
/// compares against. If SQLKit ever renames them the guard would silently
|
||||
/// skip Postgres too, and the tuning would quietly never be applied.
|
||||
@Test("the SQLite dialect reports the name the guard excludes")
|
||||
func dialectNameIsStable() async throws {
|
||||
let app = try await Application.make(.testing)
|
||||
app.databases.use(.sqlite(.memory), as: .sqlite)
|
||||
let sql = try #require(app.db as? any SQLDatabase)
|
||||
#expect(sql.dialect.name == "sqlite")
|
||||
try await app.asyncShutdown()
|
||||
}
|
||||
|
||||
/// The registration list is what actually runs at boot, so a migration
|
||||
/// that is Postgres-only still has to be safe to register everywhere.
|
||||
@Test("the tuning migration is in the boot schema")
|
||||
func tuningIsRegistered() {
|
||||
#expect(RelayMigrations.all().contains { $0 is TuneUserDataAutovacuum })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user