You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
EF variant tests: parallel cases share one SqliteConnection and now and then fail with 'database is locked' #148
QueryTranslationVariantTests keeps one in-memory SQLite database per model in a static ConcurrentDictionary<string, Lazy<(SqliteConnection, Exception?)>> ("seeded once and only read"). Every case whose key matches opens a DbContext on that same open SqliteConnection. xunit runs the theory's rows in parallel, so two rows can run commands on one connection at once. SqliteConnection isn't thread-safe, and SQLite then answers SQLITE_BUSY.
Seen once, on PR #141's Linux leg at 5a22140 (run 40889709782, 2026-10-29). All of release/2.3.0 was merged in at that point, and the PR doesn't touch EF:
failed QueryTranslationVariantTests.A_query_over_a_value_object_answers_as_the_same_query_over_the_wrapped_type(kind: "decimal", conversion: TypeKit, site: Nullable, accessor: Itself, op: OrderBy)
Shouldly.ShouldAssertException : refused where the same query over the wrapped type answers [<null>, 1.5, 10.0]: Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 5: 'database is locked ...'
The same Windows leg passed, and re-running the failed job is the workaround. The cache predates #142. #142 shares AnswersAsTheWrappedType with the new SQL Server leg and adds more catalogue values, which may make overlaps more likely. That isn't measured.
The variant tests are this repo's characteristic guard (decision 95), and a refusal that comes from the harness reads as a translation difference. A flake here also blocks the release workflow, which runs the whole suite.
Proposal
Give each case its own access: a SemaphoreSlim or lock per cached connection around the context's use, or a connection per case on a shared-cache database (Data Source=<key>;Mode=Memory;Cache=Shared, seeded once, kept alive by the cached connection). Measure the suite's time before and after, since the cache exists for speed.
Done when
No two cases run on one SqliteConnection at once. A test stress-runs a cached model's cases in parallel, about 200 times, and is seeded with the lock removed (it fails), then restored.
The suite's time on the Linux leg is stated before and after in the PR.
Seen twice in a row on PR #141's run 40889709782 (Linux leg, head 5a22140, a diff that touches no EF code): attempt 1 on a decimalOrderBy case, attempt 2 on a string case (refused where the same query over the wrapped type answers [<null>, Bravo, charlie]: ... SQLite Error 5: 'database is locked'). Windows passed both times. A rerun is no longer a reliable answer for a PR's Linux leg.
Cause found: since #142, SqlServerQueryVariantTests reads QueryTranslationVariantTests' cached SQLite connections through QueryTranslationVariantTests.Run from its own collection, so the two classes use one connection at once. Within the SQLite class the cases already ran one after another. That's why only the Linux leg, the one that runs the Docker classes, fails. It failed twice on #141's run 40889709782. Fix in #150: a lock per cached connection, and a parallel stress test seeded and caught.
Fixed in #150, merged into release/2.3.0 (99c6032): each read of a cached SQLite database holds the connection's lock; the parallel stress test fails without it.
Activity
CaffeinatedCoder commentedon Oct 29, 2026
Seen twice in a row on PR #141's run 40889709782 (Linux leg, head 5a22140, a diff that touches no EF code): attempt 1 on a
decimalOrderBycase, attempt 2 on a string case (refused where the same query over the wrapped type answers [<null>, Bravo, charlie]: ... SQLite Error 5: 'database is locked'). Windows passed both times. A rerun is no longer a reliable answer for a PR's Linux leg.CaffeinatedCoder commentedon Oct 29, 2026
Cause found: since #142,
SqlServerQueryVariantTestsreadsQueryTranslationVariantTests' cached SQLite connections throughQueryTranslationVariantTests.Runfrom its own collection, so the two classes use one connection at once. Within the SQLite class the cases already ran one after another. That's why only the Linux leg, the one that runs the Docker classes, fails. It failed twice on #141's run 40889709782. Fix in #150: a lock per cached connection, and a parallel stress test seeded and caught.CaffeinatedCoder commentedon Oct 29, 2026
Fixed in #150, merged into
release/2.3.0(99c6032): each read of a cached SQLite database holds the connection's lock; the parallel stress test fails without it.