fix(external-system-gateway): resolve ExternalSystemGateway-004..010 — honour retry settings, dispose HTTP messages, fix URL building, truncate error bodies, fix connection leak

This commit is contained in:
Joseph Doherty
2026-05-16 21:11:24 -04:00
parent 8c67ffad2a
commit 2502e4d10a
5 changed files with 615 additions and 52 deletions

View File

@@ -45,11 +45,29 @@ public class DatabaseGateway : IDatabaseGateway
throw new InvalidOperationException($"Database connection '{connectionName}' not found");
}
var connection = new SqlConnection(definition.ConnectionString);
await connection.OpenAsync(cancellationToken);
var connection = CreateConnection(definition.ConnectionString);
try
{
await connection.OpenAsync(cancellationToken);
}
catch
{
// OpenAsync failed (unreachable server, bad credentials, cancellation) —
// dispose the just-created connection before the exception propagates so
// it is not leaked (ExternalSystemGateway-010).
await connection.DisposeAsync();
throw;
}
return connection;
}
/// <summary>
/// Creates the underlying ADO.NET connection for a connection string. Virtual so
/// tests can substitute a connection whose <c>OpenAsync</c> fails.
/// </summary>
internal virtual DbConnection CreateConnection(string connectionString) =>
new SqlConnection(connectionString);
/// <summary>
/// Submits a SQL write to the store-and-forward engine for reliable delivery.
/// </summary>
@@ -78,12 +96,15 @@ public class DatabaseGateway : IDatabaseGateway
Parameters = parameters
});
// The per-connection retry settings are passed through verbatim — a
// configured MaxRetries of 0 means "never retry" and must NOT be
// collapsed to the S&F default (ExternalSystemGateway-004).
await _storeAndForward.EnqueueAsync(
StoreAndForwardCategory.CachedDbWrite,
connectionName,
payload,
originInstanceName,
definition.MaxRetries > 0 ? definition.MaxRetries : null,
definition.MaxRetries,
definition.RetryDelay > TimeSpan.Zero ? definition.RetryDelay : null);
}