Skip to content

EF variant tests: parallel cases share one SqliteConnection and now and then fail with 'database is locked' #148

Closed

Description

@CaffeinatedCoder

Problem

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.

Activity

  1. added this to the 2.3.0 milestone on Oct 29, 2026
  2. added
    testsAdds or changes tests only (tests, probes, harnesses); no library code
    package/efcoreAffects CodoMetis.TypeKit.EntityFrameworkCore
    on Oct 29, 2026
  3. CaffeinatedCoder commented on Oct 29, 2026

    @CaffeinatedCoder
    OwnerAuthor

    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 decimal OrderBy 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.

  4. CaffeinatedCoder commented on Oct 29, 2026

    @CaffeinatedCoder
    OwnerAuthor

    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.

  5. CaffeinatedCoder commented on Oct 29, 2026

    @CaffeinatedCoder
    OwnerAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package/efcoreAffects CodoMetis.TypeKit.EntityFrameworkCoretestsAdds or changes tests only (tests, probes, harnesses); no library code

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions