fix(historian): QuestDB final-review fix wave (I-1, M-1, M-3, M-5, M-8, M-9)
- I-1: QuestDbConnection caps Npgsql Maximum Pool Size at 24 (DefaultMaxPoolSize) when the connection string does not set it; an explicit value is kept. The effective value is logged once at Information. - M-1: QuestDbTagCatalog.EnsureAsync waits for the tag table's sequenced commits and re-checks before inserting, so it no longer overwrites a provisioner row (unit/description) that is committed but not yet applied. Integration test reproduces the clobber against real QuestDB (falsified). - M-3: the pre-1970 drop Warning is rate-limited per tag (once per 10 min); DroppedPre1970 still counts every sample. - M-5: QuestDbHistorianProvider is IAsyncDisposable/IDisposable and disposes the shared Npgsql pool; Program.cs registers the provider through a factory and every seam resolves it from the container, so DI disposes it at shutdown. - M-8, M-9: docs fixes (HistoryUpdate transaction wording; folded duplicate HistoryUpdate paragraph). Pool-size docs updated for the new default cap. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -18,8 +18,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb;
|
||||
/// validation and no I/O, so resolving the provider for a backend nobody consumes never fails on its options
|
||||
/// (they are validated, consumer-gated, by the host's <c>ServerHistorianOptionsValidator</c>). The first
|
||||
/// <c>Create*</c> call's service provider supplies the logger factory the shared pieces log through.
|
||||
/// <para>The provider owns the shared connection pool and disposes it (IAsyncDisposable/IDisposable); the host
|
||||
/// registers the provider through a DI factory so the container disposes it at shutdown.</para>
|
||||
/// </remarks>
|
||||
public sealed class QuestDbHistorianProvider : IHistorianBackendProvider
|
||||
public sealed class QuestDbHistorianProvider : IHistorianBackendProvider, IAsyncDisposable, IDisposable
|
||||
{
|
||||
private static readonly FrozenSet<HistorianValueKind> Kinds =
|
||||
Enum.GetValues<HistorianValueKind>().ToFrozenSet();
|
||||
@@ -27,6 +29,7 @@ public sealed class QuestDbHistorianProvider : IHistorianBackendProvider
|
||||
private readonly QuestDbHistorianOptions _options;
|
||||
private readonly object _initLock = new();
|
||||
private Shared? _shared;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>Creates the provider over the <c>ServerHistorian:QuestDb</c> options (validated elsewhere).</summary>
|
||||
/// <param name="options">The <c>ServerHistorian:QuestDb</c> options.</param>
|
||||
@@ -51,7 +54,8 @@ public sealed class QuestDbHistorianProvider : IHistorianBackendProvider
|
||||
{
|
||||
if (_shared is { } raced) return raced;
|
||||
var loggers = Loggers(sp);
|
||||
var conn = new QuestDbConnection(_options);
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
var conn = new QuestDbConnection(_options, loggers.CreateLogger<QuestDbConnection>());
|
||||
var bootstrap = new QuestDbSchemaBootstrapper(conn, _options, loggers.CreateLogger<QuestDbSchemaBootstrapper>());
|
||||
var wal = new QuestDbWalVisibility(conn, _options.VisibilityTimeout, loggers.CreateLogger<QuestDbWalVisibility>());
|
||||
var shared = new Shared(conn, bootstrap, new QuestDbTagCatalog(conn, bootstrap, wal), wal);
|
||||
@@ -101,6 +105,28 @@ public sealed class QuestDbHistorianProvider : IHistorianBackendProvider
|
||||
Loggers(sp).CreateLogger<QuestDbHistorianDataUpdater>());
|
||||
}
|
||||
|
||||
/// <summary>Disposes the shared connection pool, when one was built. Idempotent.</summary>
|
||||
/// <returns>A task completing when the pool is disposed.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (TakeForDispose() is { } conn) await conn.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>Disposes the shared connection pool, when one was built. Idempotent.</summary>
|
||||
public void Dispose() => TakeForDispose()?.Dispose();
|
||||
|
||||
private QuestDbConnection? TakeForDispose()
|
||||
{
|
||||
lock (_initLock)
|
||||
{
|
||||
if (_disposed) return null;
|
||||
_disposed = true;
|
||||
var conn = _shared?.Conn;
|
||||
_shared = null;
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record Shared(
|
||||
QuestDbConnection Conn, QuestDbSchemaBootstrapper Bootstrap, QuestDbTagCatalog Catalog, QuestDbWalVisibility Wal);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user