fix(questdb): disable Npgsql GSS encryption probe unless the connection string sets it (lmxopcua#758)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
QuestDB speaks plain PG-wire; Npgsql's default GSS Encryption Mode=Prefer made Linux central boots log 'Cannot load library libgssapi_krb5.so.2'. An explicit 'GSS Encryption Mode' in the connection string is kept. Not re-verified on a Linux host here; the Postgres Sql driver is left alone (Kerberos may be wanted). Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -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
|
||||
/// <summary>The Npgsql <c>Maximum Pool Size</c> in effect (the connection string's own value, else <see cref="DefaultMaxPoolSize"/>).</summary>
|
||||
internal int EffectiveMaxPoolSize { get; }
|
||||
|
||||
/// <summary>The Npgsql <c>GSS Encryption Mode</c> in effect (the connection string's own value, else <c>Disable</c>, #758).</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
+18
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user