diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb/QuestDbConnection.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb/QuestDbConnection.cs index 54d430209..f04d4e682 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb/QuestDbConnection.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb/QuestDbConnection.cs @@ -42,6 +42,13 @@ internal sealed class QuestDbConnection : IAsyncDisposable, IDisposable var explicitPool = HasExplicitMaxPoolSize(options.ConnectionString); if (!explicitPool) csb.MaxPoolSize = DefaultMaxPoolSize; EffectiveMaxPoolSize = csb.MaxPoolSize; + // #758: QuestDB speaks plain PG-wire with no GSS support. Npgsql's default GSS Encryption Mode=Prefer probes + // for GSSAPI on every new physical connection, which on Linux logs "Cannot load library libgssapi_krb5.so.2" + // when the Kerberos client library is not installed. The probe falls back cleanly, so this only silences a + // misleading line; an explicit "GSS Encryption Mode" in the connection string is always kept. + if (!HasExplicitKey(options.ConnectionString, "GSSEncryptionMode")) + csb.GssEncryptionMode = GssEncryptionMode.Disable; + EffectiveGssEncryptionMode = csb.GssEncryptionMode; (log ?? NullLogger.Instance).LogInformation( "QuestDB historian connection pool: Maximum Pool Size = {MaxPoolSize} ({Source}).", EffectiveMaxPoolSize, explicitPool ? "from the connection string" : "default cap"); @@ -51,17 +58,23 @@ internal sealed class QuestDbConnection : IAsyncDisposable, IDisposable /// The Npgsql Maximum Pool Size in effect (the connection string's own value, else ). internal int EffectiveMaxPoolSize { get; } + /// The Npgsql GSS Encryption Mode in effect (the connection string's own value, else Disable, #758). + internal GssEncryptionMode EffectiveGssEncryptionMode { get; } + // Npgsql accepts "Maximum Pool Size" and "MaxPoolSize" (case-insensitive); a generic parse keeps the caller's own // keywords so "was it set" is answered by the string itself, not by the builder's default. - private static bool HasExplicitMaxPoolSize(string connectionString) + private static bool HasExplicitMaxPoolSize(string connectionString) => + HasExplicitKey(connectionString, "MaxPoolSize", "MaximumPoolSize"); + + // Spaces are ignored in keywords ("GSS Encryption Mode" == "GSSEncryptionMode"); matching is case-insensitive. + private static bool HasExplicitKey(string connectionString, params string[] normalizedKeys) { var generic = new System.Data.Common.DbConnectionStringBuilder { ConnectionString = connectionString }; foreach (string key in generic.Keys) { - var k = key.Replace(" ", "", StringComparison.Ordinal); - if (string.Equals(k, "MaxPoolSize", StringComparison.OrdinalIgnoreCase) - || string.Equals(k, "MaximumPoolSize", StringComparison.OrdinalIgnoreCase)) - return true; + var k = key.Replace(" ", "", StringComparison.Ordinal).Replace("_", "", StringComparison.Ordinal); + foreach (var wanted in normalizedKeys) + if (string.Equals(k, wanted, StringComparison.OrdinalIgnoreCase)) return true; } return false; } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.Tests/QuestDbConnectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.Tests/QuestDbConnectionTests.cs index afc09155e..0ed41bf55 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.Tests/QuestDbConnectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.QuestDb.Tests/QuestDbConnectionTests.cs @@ -146,6 +146,24 @@ public sealed class QuestDbConnectionTests return c.EffectiveMaxPoolSize; } + // ---- GSS probe (#758) ------------------------------------------------------------------------------------------- + + private static Npgsql.GssEncryptionMode GssOf(string cs) + { + using var c = new QuestDbConnection(new QuestDbHistorianOptions { ConnectionString = cs }); + return c.EffectiveGssEncryptionMode; + } + + [Fact] + public void An_unset_GSS_Encryption_Mode_is_disabled() => + GssOf("Host=127.0.0.1;Port=1").ShouldBe(Npgsql.GssEncryptionMode.Disable); + + [Theory] + [InlineData("Host=127.0.0.1;Port=1;GSS Encryption Mode=Prefer", Npgsql.GssEncryptionMode.Prefer)] + [InlineData("Host=127.0.0.1;Port=1;GssEncryptionMode=Require", Npgsql.GssEncryptionMode.Require)] + public void An_explicit_GSS_Encryption_Mode_is_kept(string cs, Npgsql.GssEncryptionMode expected) => + GssOf(cs).ShouldBe(expected); + [Fact] public void An_unset_Maximum_Pool_Size_is_capped_at_24() => PoolOf("Host=127.0.0.1;Port=1").ShouldBe(24);