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:
master
2026-04-06 08:51:04 +03:00
parent 517fa0a92d
commit ccdfd41e4f
64 changed files with 625 additions and 178 deletions

View File

@@ -123,6 +123,7 @@ static IResolutionCacheService CreateResolutionCacheService(IServiceProvider ser
redisOptions.ConnectTimeout = 500;
redisOptions.SyncTimeout = 500;
redisOptions.AsyncTimeout = 500;
redisOptions.ClientName ??= "stellaops-binaryindex-resolution-cache";
var multiplexer = ConnectionMultiplexer.Connect(redisOptions);
_ = multiplexer.GetDatabase().Ping();

View File

@@ -5,6 +5,7 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| Task ID | Status | Notes |
| --- | --- | --- |
| SPRINT_20260405_011-XPORT-VALKEY | DONE | `docs/implplan/SPRINT_20260405_011___Libraries_transport_pooling_and_attribution_hardening.md`: named the BinaryIndex resolution-cache Valkey client. |
| QA-BINARYINDEX-VERIFY-029 | DONE | SPRINT_20260211_033 run-001: `patch-coverage-tracking` passed Tier 0/1/2 with patch-coverage controller behavioral evidence (including post-create coverage updates) and was moved to `docs/features/checked/binaryindex/patch-coverage-tracking.md`. |
| QA-BINARYINDEX-VERIFY-028 | BLOCKED | SPRINT_20260211_033: `ml-function-embedding-service` is actively owned by another lane (`run-001` in progress); this lane terminalized collision as `skipped` (`owned_by_other_agent`) per FLOW 0.1. |
| QA-BINARYINDEX-VERIFY-026 | DONE | SPRINT_20260211_033 run-002: `known-build-binary-catalog` passed Tier 0/1/2 with Build-ID/SHA256/assertion/cache/method-mapping behavioral evidence; dossier verified in `docs/features/checked/binaryindex/known-build-binary-catalog.md`. |

View File

@@ -10,14 +10,14 @@ namespace StellaOps.BinaryIndex.Validation.Persistence;
/// <summary>
/// PostgreSQL repository for match results.
/// </summary>
public sealed class MatchResultRepository : IMatchResultRepository
public sealed class MatchResultRepository : IMatchResultRepository, IAsyncDisposable
{
private readonly string _connectionString;
private readonly NpgsqlDataSource _dataSource;
private readonly JsonSerializerOptions _jsonOptions;
public MatchResultRepository(string connectionString)
{
_connectionString = connectionString;
_dataSource = CreateDataSource(connectionString);
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
@@ -30,8 +30,7 @@ public sealed class MatchResultRepository : IMatchResultRepository
{
if (results.Count == 0) return;
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync(ct);
await using var conn = await _dataSource.OpenConnectionAsync(ct);
await using var transaction = await conn.BeginTransactionAsync(ct);
@@ -105,8 +104,7 @@ public sealed class MatchResultRepository : IMatchResultRepository
/// <inheritdoc/>
public async Task<IReadOnlyList<MatchResult>> GetForRunAsync(Guid runId, CancellationToken ct = default)
{
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync(ct);
await using var conn = await _dataSource.OpenConnectionAsync(ct);
const string query = """
SELECT
@@ -215,4 +213,18 @@ public sealed class MatchResultRepository : IMatchResultRepository
string ExpectedName, string? ExpectedDemangledName, long ExpectedAddress, long? ExpectedSize, string ExpectedBuildId, string ExpectedBinaryName,
string? ActualName, string? ActualDemangledName, long? ActualAddress, long? ActualSize, string? ActualBuildId, string? ActualBinaryName,
string Outcome, double? MatchScore, string? Confidence, string? InferredCause, string? MismatchDetail, double? MatchDurationMs);
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
private static NpgsqlDataSource CreateDataSource(string connectionString)
{
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString)
{
ApplicationName = "stellaops-binaryindex-match-results"
};
return new NpgsqlDataSourceBuilder(connectionStringBuilder.ConnectionString).Build();
}
}

View File

@@ -10,14 +10,14 @@ namespace StellaOps.BinaryIndex.Validation.Persistence;
/// <summary>
/// PostgreSQL repository for validation runs.
/// </summary>
public sealed class ValidationRunRepository : IValidationRunRepository
public sealed class ValidationRunRepository : IValidationRunRepository, IAsyncDisposable
{
private readonly string _connectionString;
private readonly NpgsqlDataSource _dataSource;
private readonly JsonSerializerOptions _jsonOptions;
public ValidationRunRepository(string connectionString)
{
_connectionString = connectionString;
_dataSource = CreateDataSource(connectionString);
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
@@ -28,8 +28,7 @@ public sealed class ValidationRunRepository : IValidationRunRepository
/// <inheritdoc/>
public async Task SaveAsync(ValidationRun run, CancellationToken ct = default)
{
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync(ct);
await using var conn = await _dataSource.OpenConnectionAsync(ct);
const string upsert = """
INSERT INTO groundtruth.validation_runs (
@@ -107,8 +106,7 @@ public sealed class ValidationRunRepository : IValidationRunRepository
/// <inheritdoc/>
public async Task<ValidationRun?> GetAsync(Guid runId, CancellationToken ct = default)
{
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync(ct);
await using var conn = await _dataSource.OpenConnectionAsync(ct);
const string query = """
SELECT
@@ -132,8 +130,7 @@ public sealed class ValidationRunRepository : IValidationRunRepository
ValidationRunFilter? filter,
CancellationToken ct = default)
{
await using var conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync(ct);
await using var conn = await _dataSource.OpenConnectionAsync(ct);
var sql = new System.Text.StringBuilder("""
SELECT id, name, status, created_at, completed_at,
@@ -264,4 +261,18 @@ public sealed class ValidationRunRepository : IValidationRunRepository
private sealed record ValidationRunSummaryRow(
Guid Id, string Name, string Status, DateTimeOffset CreatedAt, DateTimeOffset? CompletedAt,
double? MatchRate, double? F1Score, int? PairCount, int? FunctionCount, string[]? Tags);
public ValueTask DisposeAsync() => _dataSource.DisposeAsync();
private static NpgsqlDataSource CreateDataSource(string connectionString)
{
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
var connectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString)
{
ApplicationName = "stellaops-binaryindex-validation-runs"
};
return new NpgsqlDataSourceBuilder(connectionStringBuilder.ConnectionString).Build();
}
}

View File

@@ -4,6 +4,7 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| Task ID | Status | Notes |
| --- | --- | --- |
| SPRINT_20260405_011-XPORT | DONE | `docs/implplan/SPRINT_20260405_011___Libraries_transport_pooling_and_attribution_hardening.md`: named reusable PostgreSQL data sources for validation-run and match-result persistence. |
| QA-BINARYINDEX-VERIFY-023 | BLOCKED | SPRINT_20260211_033 run-001: blocked because `AGENTS.md` is missing in this module and related validation modules/tests (repo AGENTS rule 5). |
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Validation/StellaOps.BinaryIndex.Validation.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |