Skip to content

Don't clear used connection pools#2217

Merged
myieye merged 3 commits intodevelopfrom
dont-clear-used-connection-pools
Mar 23, 2026
Merged

Don't clear used connection pools#2217
myieye merged 3 commits intodevelopfrom
dont-clear-used-connection-pools

Conversation

@myieye
Copy link
Copy Markdown
Collaborator

@myieye myieye commented Mar 19, 2026

I'm hoping one of these changes will prevent the race-condition that has resulted in this exception in CI over the past weeks:

[xUnit.net 00:01:33.36]     LcmCrdt.Tests.SnapshotAtCommitServiceTests.ModifiedEntry_ShowsOriginalState [FAIL]
[xUnit.net 00:01:33.48]     LcmCrdt.Tests.SnapshotAtCommitServiceTests.DeletedEntry_StillAppearsInSnapshot [FAIL]
[xUnit.net 00:01:33.59]     LcmCrdt.Tests.SnapshotAtCommitServiceTests.NonExistentCommit_ReturnsNull [FAIL]
[xUnit.net 00:01:33.74]     LcmCrdt.Tests.SnapshotAtCommitServiceTests.CanRegenerateSnapshotAtPreviousCommit [FAIL]
[xUnit.net 00:01:33.85]     LcmCrdt.Tests.SnapshotAtCommitServiceTests.LatestCommit_ReturnsCurrentState [FAIL]
[xUnit.net 00:01:33.95]     LcmCrdt.Tests.SnapshotAtCommitServiceTests.PreserveAllFieldWorksCommits_IncludesFieldWorksChangesInSnapshot [FAIL]
Error: System.ObjectDisposedException : Cannot access a disposed object.
Object name: 'SQLitePCL.sqlite3'.
  Failed LcmCrdt.Tests.SnapshotAtCommitServiceTests.ModifiedEntry_ShowsOriginalState [14 s]
  Error Message:
   System.ObjectDisposedException : Cannot access a disposed object.
Object name: 'SQLitePCL.sqlite3'.
  Stack Trace:
     at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
   at SQLitePCL.SQLite3Provider_e_sqlite3.SQLitePCL.ISQLite3Provider.sqlite3_create_collation(sqlite3 db, Byte[] name, Object v, delegate_collation func)
   at SQLitePCL.raw.sqlite3_create_collation(sqlite3 db, String name, Object v, strdelegate_collation f)
   at Microsoft.Data.Sqlite.SqliteConnection.Open()
   at System.Data.Common.DbConnection.OpenAsync(CancellationToken cancellationToken)
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at SIL.Harmony.Db.CrdtRepository.GetEntityEntry(Type entityType, Guid entityId) in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\Db\CrdtRepository.cs:line 372
   at SIL.Harmony.Db.CrdtRepository.ProjectSnapshot(ObjectSnapshot objectSnapshot) in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\Db\CrdtRepository.cs:line 341
   at SIL.Harmony.Db.CrdtRepository.AddSnapshots(IEnumerable`1 snapshots) in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\Db\CrdtRepository.cs:line 319
   at SIL.Harmony.SnapshotWorker.UpdateSnapshots(SortedSet`1 commits) in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\SnapshotWorker.cs:line 55
   at SIL.Harmony.DataModel.UpdateSnapshots(CrdtRepository repo, SortedSet`1 commitsToApply) in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\DataModel.cs:line 217
   at SIL.Harmony.DataModel.RegenerateSnapshots() in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\DataModel.cs:line 248
   at SIL.Harmony.DataModel.RegenerateSnapshots() in D:\a\languageforge-lexbox\languageforge-lexbox\backend\harmony\src\SIL.Harmony\DataModel.cs:line 248
   at LcmCrdt.Tests.SnapshotAtCommitServiceTests.ModifiedEntry_ShowsOriginalState() in D:\a\languageforge-lexbox\languageforge-lexbox\backend\FwLite\LcmCrdt.Tests\SnapshotAtCommitServiceTests.cs:line 98
--- End of stack trace from previous location ---
Error: System.InvalidOperationException : Sequence contains no matching element

@github-actions github-actions bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Mar 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 19, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d325720b-29ee-4f90-b031-4aa67440e68a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Updated async disposal to use await using for DI scopes and DbContexts, and replaced global SQLite pool clearing with targeted SqliteConnection.ClearPool(...) for specific database files in service and tests.

Changes

Cohort / File(s) Summary
Service implementation
backend/FwLite/LcmCrdt/SnapshotAtCommitService.cs
Converted temporary DI scope and LcmCrdtDbContext usage to await using for async disposal; replaced SqliteConnection.ClearAllPools() with SqliteConnection.ClearPool(new SqliteConnection($"Data Source={forkDbPath}")).
Test teardown
backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs
Changed test DisposeAsync to call SqliteConnection.ClearPool(new SqliteConnection($"Data Source={_dbPath}")) instead of SqliteConnection.ClearAllPools().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through scopes with careful paws,

Awaiting each using, observing the laws.
One pool to clear, a gentle sweep,
No noisy sweep for others to keep.
🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: replacing global SQLite pool cleanup with targeted pool cleanup to prevent clearing connection pools that are still in use.
Description check ✅ Passed The description is directly related to the changeset, providing context about the race condition being fixed and showing the specific exception that prompted these changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dont-clear-used-connection-pools

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 19, 2026

UI unit Tests

  1 files  ±0   54 suites  ±0   26s ⏱️ ±0s
140 tests ±0  140 ✅ ±0  0 💤 ±0  0 ❌ ±0 
207 runs  ±0  207 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 75e737f. ± Comparison against base commit 0445aa3.

♻️ This comment has been updated with latest results.

@argos-ci
Copy link
Copy Markdown

argos-ci bot commented Mar 19, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Mar 23, 2026, 8:56 AM

@myieye myieye force-pushed the dont-clear-used-connection-pools branch from 83fd174 to 9a7fe6b Compare March 19, 2026 15:26
@myieye
Copy link
Copy Markdown
Collaborator Author

myieye commented Mar 19, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 19, 2026

@myieye: Sure! I'll kick off a full review of the PR right away.


🧠 Learnings used
Learnt from: rmunn
Repo: sillsdev/languageforge-lexbox PR: 2192
File: backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs:352-365
Timestamp: 2026-03-12T06:58:58.411Z
Learning: In sillsdev/languageforge-lexbox backend/FwLite/LcmCrdt, the CrdtRepository class handles tombstone (soft-delete) filtering centrally in its ProjectSnapshot method, which removes deleted entries from the EF Core DB context before SaveChanges() is called. As a result, MiniLcmApi classes (CrdtMiniLcmApi, FwDataMiniLcmApi, etc.) do NOT need to add `DeletedAt == null` filters when querying the repository — deleted objects are never present in the live DB view. Do not suggest adding such filters in code reviews of these API classes.
✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs`:
- Around line 329-334: The test teardown in DisposeAsync does not explicitly
dispose the factory-created _dbContext; update the DisposeAsync method to
explicitly dispose the DbContext instance (call await _dbContext.DisposeAsync()
or _dbContext.Dispose() if targeting sync) before closing/clearing the SQLite
connection and deleting the database so file handles are released; locate
DisposeAsync in SnapshotAtCommitServiceTests and add the explicit disposal of
the _dbContext instance (reference: _dbContext, DisposeAsync method,
IDbContextFactory) just prior to calling
_dbContext.Database.CloseConnectionAsync() or immediately at the start of
teardown.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 25015186-826f-4e8f-a7d3-02c9b8bceb03

📥 Commits

Reviewing files that changed from the base of the PR and between 0445aa3 and 9a7fe6b.

📒 Files selected for processing (2)
  • backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs
  • backend/FwLite/LcmCrdt/SnapshotAtCommitService.cs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs`:
- Line 332: The call to SqliteConnection.ClearPool(new SqliteConnection($"Data
Source={_dbPath}")) passes a newly created SqliteConnection that is never
disposed; change the test to create the SqliteConnection instance separately,
dispose it (e.g., via using or calling Dispose) after calling
SqliteConnection.ClearPool(connection), or call Dispose on the same instance you
pass to ClearPool so the SqliteConnection used in SnapshotAtCommitServiceTests
is properly cleaned up.

In `@backend/FwLite/LcmCrdt/SnapshotAtCommitService.cs`:
- Around line 71-74: The SqliteConnection instance passed into
SqliteConnection.ClearPool in SnapshotAtCommitService.cs is not disposed; create
a SqliteConnection variable for the new connection (e.g., conn = new
SqliteConnection($"Data Source={forkDbPath}")), call
SqliteConnection.ClearPool(conn), and then dispose the connection (preferably
via a using or using var) so the IDisposable is properly cleaned up; do not open
the connection before clearing the pool.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0f8a7290-35f7-417d-bafb-53bca28a3bd4

📥 Commits

Reviewing files that changed from the base of the PR and between 0445aa3 and 9a7fe6b.

📒 Files selected for processing (2)
  • backend/FwLite/LcmCrdt.Tests/SnapshotAtCommitServiceTests.cs
  • backend/FwLite/LcmCrdt/SnapshotAtCommitService.cs

@myieye myieye merged commit 6537f4c into develop Mar 23, 2026
19 checks passed
@myieye myieye deleted the dont-clear-used-connection-pools branch March 23, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant