fix(config-db): EnableRetryOnFailure fleet-wide + execution-strategy wrap of the combined-telemetry dual-write
Installs SqlServerRetryingExecutionStrategy (5 retries, 30s cap) so transient SQL faults retry transparently on every read-side path. Manual transactions aren't auto-retried, so AuditLogIngestActor's idempotent dual-write is wrapped in an explicit CreateExecutionStrategy().ExecuteAsync to become retriable. Verified empirically that existing manual transactions (BundleImporter) are unaffected — EF skips the strategy while a transaction is already active.
This commit is contained in:
+28
-1
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||
@@ -13,7 +14,7 @@ public class ServiceCollectionExtensionsTests
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddConfigurationDatabase("DataSource=:memory:");
|
||||
services.AddConfigurationDatabase("Server=unused;Database=unused;");
|
||||
|
||||
Assert.Contains(services, d => d.ServiceType == typeof(ITemplateEngineRepository));
|
||||
Assert.Contains(services, d => d.ServiceType == typeof(IAuditService));
|
||||
@@ -21,6 +22,32 @@ public class ServiceCollectionExtensionsTests
|
||||
Assert.Contains(services, d => d.ServiceType == typeof(IAuditLogRepository));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddConfigurationDatabase_Configures_RetryingExecutionStrategy()
|
||||
{
|
||||
// Task 12 (arch-review 04 S5): the central DbContext must survive transient
|
||||
// SQL faults. EnableRetryOnFailure installs a non-null ExecutionStrategyFactory
|
||||
// on the SqlServer options extension — covering every read-side path (KPI asks,
|
||||
// outbox queries, Tracking.Status, execution trees) that previously surfaced
|
||||
// transient faults raw.
|
||||
var services = new ServiceCollection();
|
||||
services.AddConfigurationDatabase("Server=unused;Database=unused;");
|
||||
using var sp = services.BuildServiceProvider();
|
||||
|
||||
var options = sp.GetRequiredService<DbContextOptions<ScadaBridgeDbContext>>();
|
||||
|
||||
// EF1001: SqlServerOptionsExtension is an internal EF surface, but it is the
|
||||
// only place the retrying-strategy factory is observable — asserting it is the
|
||||
// point of this test. Scoped suppression, not a project-wide one.
|
||||
#pragma warning disable EF1001
|
||||
var sqlExt = options.Extensions
|
||||
.OfType<Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal.SqlServerOptionsExtension>()
|
||||
.Single();
|
||||
|
||||
Assert.NotNull(sqlExt.ExecutionStrategyFactory);
|
||||
#pragma warning restore EF1001
|
||||
}
|
||||
|
||||
// The no-arg overload is [Obsolete(error: true)], so it cannot be referenced directly
|
||||
// from source — that is the compile-time guard. Invoke it via reflection to verify the
|
||||
// runtime defence-in-depth behaviour.
|
||||
|
||||
Reference in New Issue
Block a user