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:
@@ -235,15 +235,19 @@ if (hasDriver)
|
||||
// Backend selection: resolve the provider once (unknown name => OptionsValidationException naming the
|
||||
// allowed values) and build every historian seam through it.
|
||||
var historianProvider = HistorianBackendRegistry.Resolve(serverHistorianOptions, builder.Configuration);
|
||||
builder.Services.AddSingleton<IHistorianBackendProvider>(historianProvider);
|
||||
// Registered through a factory (not as an instance) so the container owns it and disposes it at shutdown
|
||||
// (the QuestDb provider's shared Npgsql pool); every seam below resolves the provider from the container
|
||||
// rather than capturing the local, so whichever seam builds the backend's shared state also makes the
|
||||
// container track the provider for disposal.
|
||||
builder.Services.AddSingleton<IHistorianBackendProvider>(_ => historianProvider);
|
||||
// HistoryUpdate is gated like HistoryRead: None unconditionally, the real holder only when enabled.
|
||||
builder.Services.AddSingleton(HistorianUpdaterHolder.None);
|
||||
if (serverHistorianOptions.Enabled)
|
||||
builder.Services.AddSingleton(sp => new HistorianUpdaterHolder(historianProvider.CreateUpdater(sp)));
|
||||
builder.Services.AddSingleton(sp => new HistorianUpdaterHolder(sp.GetRequiredService<IHistorianBackendProvider>().CreateUpdater(sp)));
|
||||
|
||||
builder.Services.AddAlarmHistorian(
|
||||
builder.Configuration,
|
||||
(_, sp) => historianProvider.CreateAlarmWriter(sp));
|
||||
(_, sp) => sp.GetRequiredService<IHistorianBackendProvider>().CreateAlarmWriter(sp));
|
||||
|
||||
// Node-local LocalDb: caches the deployed-configuration artifact so this node can boot from
|
||||
// its last-known-good config when central SQL is unreachable, and optionally replicates that
|
||||
@@ -269,7 +273,7 @@ if (hasDriver)
|
||||
// node manager's HistoryRead overrides block-bridge to. Runtime owns the gating.
|
||||
builder.Services.AddServerHistorian(
|
||||
builder.Configuration,
|
||||
(_, sp) => historianProvider.CreateDataSource(sp));
|
||||
(_, sp) => sp.GetRequiredService<IHistorianBackendProvider>().CreateDataSource(sp));
|
||||
|
||||
// Config-gated historian tag provisioning. When the ServerHistorian section is enabled this registers the
|
||||
// selected backend's provisioner (GatewayTagProvisioner -> the gateway's EnsureTags, or the SqlServer Tag-row
|
||||
@@ -280,7 +284,7 @@ if (hasDriver)
|
||||
// the SAME ServerHistorian:Enabled flag as the read path — both use the one resolved backend.
|
||||
builder.Services.AddHistorianProvisioning(
|
||||
builder.Configuration,
|
||||
(_, sp) => historianProvider.CreateProvisioning(sp));
|
||||
(_, sp) => sp.GetRequiredService<IHistorianBackendProvider>().CreateProvisioning(sp));
|
||||
|
||||
// Continuous historization of driver (non-Galaxy) tag values. Gated on ContinuousHistorization:Enabled
|
||||
// AND ServerHistorian:Enabled: the recorder drains driver-tag live values through the selected backend's
|
||||
@@ -319,7 +323,7 @@ if (hasDriver)
|
||||
// The backend's live-value writer. On Gateway it opens its OWN gRPC channel to the same gateway (a second
|
||||
// channel to a co-located sidecar is cheap — the gateway pools the historian sessions server-side); on
|
||||
// SqlServer it shares the provider's connection helper, schema bootstrapper and tag catalog.
|
||||
builder.Services.AddSingleton<IHistorianValueWriter>(sp => historianProvider.CreateValueWriter(sp));
|
||||
builder.Services.AddSingleton<IHistorianValueWriter>(sp => sp.GetRequiredService<IHistorianBackendProvider>().CreateValueWriter(sp));
|
||||
}
|
||||
|
||||
// Bind every cross-platform driver factory before AddAkka resolves IDriverFactory — replaces
|
||||
|
||||
Reference in New Issue
Block a user