test(inboundapi): root the shared in-memory SQLite keep-alive so the database cannot vanish mid-test
Script_UsingDatabase_QueriesViaGateway failed in about 2 of 27 full InboundAPI runs. Looping the suite 30 times captured the failure 4 times, always as "Internal script error". The exception underneath was SqliteException 'no such table: Machine'. Cause: the seeding helper opened its keep-alive SqliteConnection in a local. Once the helper returned, nothing referenced that connection. After a GC (likely during the Roslyn compile, under full-suite load), Microsoft.Data.Sqlite's pool reclaimed the leaked connection and closed the last connection to the shared in-memory database, which destroyed it. Fix: hold the keep-alive in a static field. InboundDatabaseHelperTests had the same latent bug and gets the same fix. The test now forces GC plus SqliteConnection.ClearAllPools() before the query. That makes the old pattern fail deterministically and the fix pass deterministically. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -940,6 +940,13 @@ public class InboundScriptExecutorTests
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IDatabaseGateway>(SeededSqliteGateway());
|
||||
using var provider = services.BuildServiceProvider();
|
||||
// Regression guard for the old flake: force the collection the full suite used to hit during
|
||||
// the script compile, then the pool reclaim that followed it. An unrooted keep-alive dies here
|
||||
// deterministically ("Internal script error"); ClearAllPools only closes IDLE connections.
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
SqliteConnection.ClearAllPools();
|
||||
|
||||
var executor = new InboundScriptExecutor(
|
||||
NullLogger<InboundScriptExecutor>.Instance, provider);
|
||||
@@ -1052,11 +1059,26 @@ public class InboundScriptExecutorTests
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static SqliteGateway SeededSqliteGateway()
|
||||
/// <summary>
|
||||
/// Keep-alive for the shared in-memory database, rooted in a static field. It used to be a
|
||||
/// local in the seeding helper: once the helper returned nothing referenced it, so after a GC
|
||||
/// (likely during the script compile under a full-suite run) Microsoft.Data.Sqlite's pool
|
||||
/// reclaimed the leaked connection, closed the last connection and destroyed the database. The
|
||||
/// query then failed with <c>no such table: Machine</c>, which the script reported as "Internal
|
||||
/// script error" (4 runs in 30, captured 2026-11-10).
|
||||
/// </summary>
|
||||
private static readonly SqliteConnection KeepAlive = OpenKeepAlive();
|
||||
|
||||
private static SqliteConnection OpenKeepAlive()
|
||||
{
|
||||
var keep = new SqliteConnection("DataSource=file:movein-exec?mode=memory&cache=shared");
|
||||
keep.Open(); // keep-alive: shared in-memory db lives until process exit
|
||||
using var cmd = keep.CreateCommand();
|
||||
keep.Open(); // lives until process exit
|
||||
return keep;
|
||||
}
|
||||
|
||||
private static SqliteGateway SeededSqliteGateway()
|
||||
{
|
||||
using var cmd = KeepAlive.CreateCommand();
|
||||
cmd.CommandText = "CREATE TABLE IF NOT EXISTS Machine(Code TEXT, SAPID TEXT); DELETE FROM Machine; INSERT INTO Machine VALUES('Z28061A','131453');";
|
||||
cmd.ExecuteNonQuery();
|
||||
return new SqliteGateway("DataSource=file:movein-exec?mode=memory&cache=shared");
|
||||
|
||||
Reference in New Issue
Block a user