Centralize Postgres connection string policy across all modules
Extract connection string building into PostgresConnectionStringPolicy so all services use consistent pooling, application_name, and timeout settings. Adopt the new policy in 20+ module DataSource/ServiceCollectionExtensions classes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,7 @@ namespace StellaOps.Doctor.WebService.Services;
|
||||
/// </summary>
|
||||
public sealed class PostgresReportStorageService : IReportStorageService, IDisposable
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
private readonly NpgsqlDataSource _dataSource;
|
||||
private readonly DoctorServiceOptions _options;
|
||||
private readonly ILogger<PostgresReportStorageService> _logger;
|
||||
private readonly Timer? _cleanupTimer;
|
||||
@@ -37,9 +37,10 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
IOptions<DoctorServiceOptions> options,
|
||||
ILogger<PostgresReportStorageService> logger)
|
||||
{
|
||||
_connectionString = configuration.GetConnectionString("StellaOps")
|
||||
var connectionString = configuration.GetConnectionString("StellaOps")
|
||||
?? configuration["Database:ConnectionString"]
|
||||
?? throw new InvalidOperationException("Database connection string not configured");
|
||||
_dataSource = CreateDataSource(connectionString);
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
|
||||
@@ -60,8 +61,7 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
var json = JsonSerializer.Serialize(report, JsonSerializerOptions.Default);
|
||||
var compressed = CompressJson(json);
|
||||
|
||||
await using var connection = new NpgsqlConnection(_connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO doctor_reports (run_id, started_at, completed_at, overall_severity,
|
||||
@@ -104,8 +104,7 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
/// <inheritdoc />
|
||||
public async Task<DoctorReport?> GetReportAsync(string runId, CancellationToken ct)
|
||||
{
|
||||
await using var connection = new NpgsqlConnection(_connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||
|
||||
const string sql = "SELECT report_json_compressed FROM doctor_reports WHERE run_id = @runId";
|
||||
|
||||
@@ -126,8 +125,7 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ReportSummaryDto>> ListReportsAsync(int limit, int offset, CancellationToken ct)
|
||||
{
|
||||
await using var connection = new NpgsqlConnection(_connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||
|
||||
const string sql = """
|
||||
SELECT run_id, started_at, completed_at, overall_severity,
|
||||
@@ -170,8 +168,7 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> DeleteReportAsync(string runId, CancellationToken ct)
|
||||
{
|
||||
await using var connection = new NpgsqlConnection(_connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||
|
||||
const string sql = "DELETE FROM doctor_reports WHERE run_id = @runId";
|
||||
|
||||
@@ -185,8 +182,7 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
/// <inheritdoc />
|
||||
public async Task<int> GetCountAsync(CancellationToken ct)
|
||||
{
|
||||
await using var connection = new NpgsqlConnection(_connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||
|
||||
const string sql = "SELECT COUNT(*) FROM doctor_reports";
|
||||
|
||||
@@ -207,8 +203,7 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
|
||||
var cutoff = DateTimeOffset.UtcNow.AddDays(-_options.ReportRetentionDays);
|
||||
|
||||
await using var connection = new NpgsqlConnection(_connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var connection = await _dataSource.OpenConnectionAsync(ct);
|
||||
|
||||
const string sql = "DELETE FROM doctor_reports WHERE created_at < @cutoff";
|
||||
|
||||
@@ -261,7 +256,20 @@ public sealed class PostgresReportStorageService : IReportStorageService, IDispo
|
||||
if (!_disposed)
|
||||
{
|
||||
_cleanupTimer?.Dispose();
|
||||
_dataSource.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static NpgsqlDataSource CreateDataSource(string connectionString)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
|
||||
|
||||
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString)
|
||||
{
|
||||
ApplicationName = "stellaops-doctor-report-storage"
|
||||
};
|
||||
|
||||
return new NpgsqlDataSourceBuilder(connectionStringBuilder.ConnectionString).Build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Completed
|
||||
|
||||
### 2026-01-12 - Sprint 001_007 Implementation
|
||||
- [x] SPRINT_20260405_011-XPORT: named the PostgreSQL report-storage data source and aligned the runtime path with the shared transport attribution guardrail.
|
||||
- [x] Created project structure following Platform WebService pattern
|
||||
- [x] Implemented DoctorServiceOptions with authority configuration
|
||||
- [x] Defined DoctorPolicies and DoctorScopes for authorization
|
||||
|
||||
Reference in New Issue
Block a user