Skip to content

Prevent pooling SQLite connections with unsettled native transactions - #39083

Merged
AndriySvyryd merged 1 commit into
dotnet:mainfrom
bazer:codex/sqlite-unsettled-transactions
Sep 26, 2026
Merged

AndriySvyryd merged 1 commit into
dotnet:mainfrom
bazer:codex/sqlite-unsettled-transactions

Conversation

@bazer

@bazer bazer commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #39082

When native rollback fails, SqliteTransaction.RollbackInternal still completes and detaches its managed transaction wrapper. Closing the connection can then return a handle with an active transaction to the pool. The next borrower can see uncommitted rows and cannot start a new transaction.

Check sqlite3_get_autocommit during deactivation, after resetting connection-specific state and inside the existing valid-handle guard. Mark an unsettled handle non-poolable so the existing return path disposes it. This preserves the rollback exception and affects only that physical connection. A connection whose native transaction has successfully ended remains reusable, including one explicitly recovered by the caller after an earlier rollback error.

Add ten regression/control cases covering sync/async rollback and disposal, rejected and successful native rollback, healthy connections in the same pool, caller recovery before close, and raw SQL transactions. Tests use SQLite's authorizer to reject ROLLBACK before execution; the failure is raised by the native engine.

Validation on Windows x64 with the pinned upstream SDK:

  • Original production code: five targeted failures and five passing controls.
  • Patched code: all ten targeted cases pass, with the identical test DLL.
  • Full Microsoft.Data.Sqlite.Tests application: 724 passed, seven existing skips, zero failures, using the standard category=failing exclusion.
  • Unchanged standalone eight-case reproduction: failed file rollbacks preserve error 23; the next checkout uses a new handle, sees only committed data and can start a transaction. Successful rollback still permits pooling; memory controls are unchanged.

Builds have zero warnings/errors. Alternate native SQLite bundles and upstream CI have not been run. There is no public API change, rollback retry or pool-wide clearing; pool return adds one native autocommit check.

AI disclosure

The issue was found and the fix, regression tests, and PR were developed with GPT-6 Astra on Extra High, with the repository owner's authorization.

Checklist

  • Contribution guidelines/walkthrough requirement confirmed by the submitter.
  • Issue filed and implementation described in Microsoft.Data.Sqlite can pool an active transaction after rollback failure #39082.
  • Maintainer approval obtained.
  • Code builds and tests pass locally as detailed above.
  • Validation confirmed by upstream CI.
  • Commit message includes the filed issue reference.
  • Regression tests added and demonstrated to fail before the production change.
  • Code follows the existing provider/pool and test patterns.

- Discard handles whose native transaction remains active at pool return.
- Preserve rollback errors and reuse of healthy or recovered connections.
- Cover sync/async rollback and disposal, plus unmanaged transactions.

Fixes dotnet#39082

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

No unresolved review issues remain, and the supplied validation passed.

Review effort: Lite
Findings: None

What changed in this PR

Prevents SQLite connections with active native transactions from being returned to the pool after rollback failures.

Changes:

  • Detects unsettled native transactions and disposes affected handles.
  • Adds regression and control coverage for rollback, disposal, recovery, and raw SQL scenarios.
File Description
test/​Microsoft.Data.Sqlite.Tests/​SqliteConnectionFactoryTest.cs Adds pooling and transaction regression coverage.
src/​Microsoft.Data.Sqlite.Core/​SqliteConnectionInternal.cs Rejects handles with active native transactions from pooling.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@AndriySvyryd AndriySvyryd self-assigned this Sep 26, 2026
@AndriySvyryd
AndriySvyryd merged commit 7b02489 into dotnet:main Sep 26, 2026
34 of 35 checks passed
@AndriySvyryd

Copy link
Copy Markdown
Member

Thanks for your contribution!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Microsoft.Data.Sqlite can pool an active transaction after rollback failure

3 participants