bug(transport): bundle import throws on real MS SQL — user-initiated transaction under EnableRetryOnFailure #28

Closed
opened 2026-07-23 11:52:30 -04:00 by dohertj2 · 1 comment
Owner

Severity: High (Transport #24 bundle import is broken on any real SQL Server) · Area: Transport / Configuration Database · Found: ClusterClient→gRPC Phase 1 rig gating, 2026-07-23

What

BundleImporter opens a user-initiated transaction in its apply path:

  • src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs:1298await _dbContext.Database.BeginTransactionAsync(ct)

while the central ConfigurationDb context is configured with a retrying execution strategy:

  • src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ServiceCollectionExtensions.cs:33sql.EnableRetryOnFailure(...)

On real MS SQL, SqlServerRetryingExecutionStrategy rejects user-initiated transactions (The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user-initiated transactions), so every bundle import fails.

Why the test suite is green

Transport.Tests uses the in-memory EF provider, which has no retrying execution strategy and where BeginTransactionAsync is a no-op — so the whole import suite passes while the production path is broken.

Suggested fix

Wrap the transactional section in the context's execution strategy so the strategy owns the transaction:

var strategy = _dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () => { /* BeginTransaction + Apply* + SaveChanges + Commit */ });

Add a real-SQL integration test (or a fake with a retrying strategy) so the suite catches regressions of this class.

**Severity:** High (Transport #24 bundle import is broken on any real SQL Server) · **Area:** Transport / Configuration Database · **Found:** ClusterClient→gRPC Phase 1 rig gating, 2026-07-23 ## What `BundleImporter` opens a **user-initiated transaction** in its apply path: - `src/ZB.MOM.WW.ScadaBridge.Transport/Import/BundleImporter.cs:1298` — `await _dbContext.Database.BeginTransactionAsync(ct)` while the central `ConfigurationDb` context is configured with a **retrying execution strategy**: - `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/ServiceCollectionExtensions.cs:33` — `sql.EnableRetryOnFailure(...)` On real MS SQL, `SqlServerRetryingExecutionStrategy` rejects user-initiated transactions (`The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user-initiated transactions`), so **every bundle import fails**. ## Why the test suite is green `Transport.Tests` uses the in-memory EF provider, which has **no** retrying execution strategy and where `BeginTransactionAsync` is a **no-op** — so the whole import suite passes while the production path is broken. ## Suggested fix Wrap the transactional section in the context's execution strategy so the strategy owns the transaction: ```csharp var strategy = _dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { /* BeginTransaction + Apply* + SaveChanges + Commit */ }); ``` Add a real-SQL integration test (or a fake with a retrying strategy) so the suite catches regressions of this class.
Author
Owner

Fixed in c4dcd9bc (on main @ 8524a7f7, pushed to origin). BundleImporter.ApplyAsync now runs the transactional apply through _dbContext.Database.CreateExecutionStrategy().ExecuteAsync, so the strategy owns the BeginTransaction→apply→Commit unit — no more user-initiated transaction under EnableRetryOnFailure. Regression test BundleImporterRetryingStrategyTests runs the import on SQLite under a retrying execution strategy: it fails with the exact production error ('...does not support user-initiated transactions.') on the pre-fix code and passes after. 104 Transport integration + 154 unit tests green.

Fixed in c4dcd9bc (on main @ 8524a7f7, pushed to origin). BundleImporter.ApplyAsync now runs the transactional apply through _dbContext.Database.CreateExecutionStrategy().ExecuteAsync, so the strategy owns the BeginTransaction→apply→Commit unit — no more user-initiated transaction under EnableRetryOnFailure. Regression test BundleImporterRetryingStrategyTests runs the import on SQLite under a retrying execution strategy: it fails with the exact production error ('...does not support user-initiated transactions.') on the pre-fix code and passes after. 104 Transport integration + 154 unit tests green.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/ScadaBridge#28