From e27a2d1ea1a9eb0ae6e9d297786752b701e9cc55 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Thu, 11 Dec 2025 12:37:11 +0530 Subject: [PATCH 01/13] Initial fix for OpenNewConnector --- src/Npgsql/ClusterAwareDataSource.cs | 10 ++- src/Npgsql/MultiHostDataSourceWrapper.cs | 5 ++ src/Npgsql/NpgsqlDataSource.cs | 5 +- src/Npgsql/NpgsqlMultiHostDataSource.cs | 4 ++ src/Npgsql/PoolingDataSource.cs | 4 ++ src/Npgsql/PublicAPI.Unshipped.txt | 1 + src/Npgsql/UnpooledDataSource.cs | 4 ++ src/Npgsql/YBPoolingWrapperDataSource.cs | 80 ++++++++++++++++++++++++ test/Npgsql.Tests/YBLoadBalancerTests.cs | 77 +++++++++++++++++------ 9 files changed, 168 insertions(+), 22 deletions(-) create mode 100644 src/Npgsql/YBPoolingWrapperDataSource.cs diff --git a/src/Npgsql/ClusterAwareDataSource.cs b/src/Npgsql/ClusterAwareDataSource.cs index e94e8e4519..4bb59888de 100644 --- a/src/Npgsql/ClusterAwareDataSource.cs +++ b/src/Npgsql/ClusterAwareDataSource.cs @@ -223,7 +223,7 @@ protected virtual void CreatePool(Dictionary hostsmap) var poolSettings = settings.Clone(); poolSettings.Host = host.Key; _connectionLogger.LogDebug("Adding {host} to connection pool", poolSettings.Host); - NpgsqlDataSource poolnew = settings.Pooling? new PoolingDataSource(poolSettings, dataSourceConfig): new UnpooledDataSource(poolSettings, dataSourceConfig); + NpgsqlDataSource poolnew = settings.Pooling? new YBPoolingWrapperDataSource(poolSettings, dataSourceConfig): new UnpooledDataSource(poolSettings, dataSourceConfig); _pools.Add(poolnew); if (host.Value.Equals("primary", StringComparison.OrdinalIgnoreCase)) { @@ -591,6 +591,10 @@ internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnect internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => throw new NotImplementedException(); + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, + NpgsqlConnectionStringBuilder settings) => + throw new NotImplementedException(); + internal override void Return(NpgsqlConnector connector) { var host = connector.Host; @@ -604,6 +608,8 @@ internal override void Return(NpgsqlConnector connector) } } UpdateConnectionMap(poolIndex, -1); + // Return the connector back to the pool to avoid memory leak + _pools[poolIndex].Return(connector); } /// @@ -786,7 +792,7 @@ static TargetSessionAttributes GetTargetSessionAttributes(NpgsqlConnection conne } else { - connector = await pool.OpenNewConnector(conn, new NpgsqlTimeout(timeoutPerHost), async, cancellationToken).ConfigureAwait(false); + connector = await pool.OpenNewConnector(conn, new NpgsqlTimeout(timeoutPerHost), async, cancellationToken, settings).ConfigureAwait(false); if (connector is not null) { if (databaseState == DatabaseState.Unknown) diff --git a/src/Npgsql/MultiHostDataSourceWrapper.cs b/src/Npgsql/MultiHostDataSourceWrapper.cs index c347e81bb8..f819d82149 100644 --- a/src/Npgsql/MultiHostDataSourceWrapper.cs +++ b/src/Npgsql/MultiHostDataSourceWrapper.cs @@ -38,6 +38,11 @@ internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnect => throw new NpgsqlException("Npgsql bug: trying to get an idle connector from " + nameof(MultiHostDataSourceWrapper)); internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => throw new NpgsqlException("Npgsql bug: trying to open a new connector from " + nameof(MultiHostDataSourceWrapper)); + + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, + NpgsqlConnectionStringBuilder settings) => + throw new System.NotImplementedException(); + internal override void Return(NpgsqlConnector connector) => wrappedSource.Return(connector); diff --git a/src/Npgsql/NpgsqlDataSource.cs b/src/Npgsql/NpgsqlDataSource.cs index bd66d6e230..b99e4e17ee 100644 --- a/src/Npgsql/NpgsqlDataSource.cs +++ b/src/Npgsql/NpgsqlDataSource.cs @@ -25,7 +25,7 @@ public abstract class NpgsqlDataSource : DbDataSource /// Contains the connection string returned to the user from /// after the connection has been opened. Does not contain the password unless Persist Security Info=true. /// - internal NpgsqlConnectionStringBuilder Settings { get; } + internal NpgsqlConnectionStringBuilder Settings { get; set; } internal NpgsqlDataSourceConfiguration Configuration { get; } internal NpgsqlLoggingConfiguration LoggingConfiguration { get; } @@ -402,6 +402,9 @@ internal abstract ValueTask Get( internal abstract ValueTask OpenNewConnector( NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken); + internal abstract ValueTask OpenNewConnector( + NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, NpgsqlConnectionStringBuilder settings); + internal abstract void Return(NpgsqlConnector connector); internal abstract bool OwnsConnectors { get; } diff --git a/src/Npgsql/NpgsqlMultiHostDataSource.cs b/src/Npgsql/NpgsqlMultiHostDataSource.cs index 61a6810133..0cf350a863 100644 --- a/src/Npgsql/NpgsqlMultiHostDataSource.cs +++ b/src/Npgsql/NpgsqlMultiHostDataSource.cs @@ -360,6 +360,10 @@ int GetRoundRobinIndex() } } + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, + NpgsqlConnectionStringBuilder settings) => + throw new NotImplementedException(); + internal override void Return(NpgsqlConnector connector) => throw new NpgsqlException("Npgsql bug: a connector was returned to " + nameof(NpgsqlMultiHostDataSource)); diff --git a/src/Npgsql/PoolingDataSource.cs b/src/Npgsql/PoolingDataSource.cs index 6910ee60da..accfb92ddc 100644 --- a/src/Npgsql/PoolingDataSource.cs +++ b/src/Npgsql/PoolingDataSource.cs @@ -320,6 +320,10 @@ bool CheckIdleConnector([NotNullWhen(true)] NpgsqlConnector? connector) return null; } + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, + NpgsqlConnectionStringBuilder settings) => + throw new NotImplementedException(); + internal sealed override void Return(NpgsqlConnector connector) { Debug.Assert(!connector.InTransaction); diff --git a/src/Npgsql/PublicAPI.Unshipped.txt b/src/Npgsql/PublicAPI.Unshipped.txt index 8d6987bb4b..8d8af046d1 100644 --- a/src/Npgsql/PublicAPI.Unshipped.txt +++ b/src/Npgsql/PublicAPI.Unshipped.txt @@ -76,3 +76,4 @@ YBNpgsql.NpgsqlConnection.ReloadTypesAsync(System.Threading.CancellationToken ca *REMOVED*YBNpgsql.NpgsqlSlimDataSourceBuilder.MapComposite(string? pgName = null, YBNpgsql.INpgsqlNameTranslator? nameTranslator = null) -> YBNpgsql.TypeMapping.INpgsqlTypeMapper! *REMOVED*YBNpgsql.NpgsqlSlimDataSourceBuilder.MapEnum(System.Type! clrType, string? pgName = null, YBNpgsql.INpgsqlNameTranslator? nameTranslator = null) -> YBNpgsql.TypeMapping.INpgsqlTypeMapper! *REMOVED*YBNpgsql.NpgsqlSlimDataSourceBuilder.MapEnum(string? pgName = null, YBNpgsql.INpgsqlNameTranslator? nameTranslator = null) -> YBNpgsql.TypeMapping.INpgsqlTypeMapper! + diff --git a/src/Npgsql/UnpooledDataSource.cs b/src/Npgsql/UnpooledDataSource.cs index b6f7d1b4ff..8cc2f0b9e0 100644 --- a/src/Npgsql/UnpooledDataSource.cs +++ b/src/Npgsql/UnpooledDataSource.cs @@ -43,6 +43,10 @@ internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnect NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => new((NpgsqlConnector?)null); + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, + NpgsqlConnectionStringBuilder settings) => + throw new System.NotImplementedException(); + internal override void Return(NpgsqlConnector connector) { Interlocked.Decrement(ref _numConnectors); diff --git a/src/Npgsql/YBPoolingWrapperDataSource.cs b/src/Npgsql/YBPoolingWrapperDataSource.cs new file mode 100644 index 0000000000..d3f824101e --- /dev/null +++ b/src/Npgsql/YBPoolingWrapperDataSource.cs @@ -0,0 +1,80 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using YBNpgsql.Internal; +using YBNpgsql.Util; + +namespace YBNpgsql; + +/// +/// +/// +sealed class YBPoolingWrapperDataSource: PoolingDataSource +{ + static ConcurrentDictionary connStringToConnectorsMap = null!; + internal YBPoolingWrapperDataSource(NpgsqlConnectionStringBuilder settings, NpgsqlDataSourceConfiguration dataSourceConfiguration) : + base(settings, dataSourceConfiguration) + { + connStringToConnectorsMap = new ConcurrentDictionary(); + } + + internal override async ValueTask OpenNewConnector( + NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, NpgsqlConnectionStringBuilder originalConnString) + { + var originalConnStringCopy = originalConnString.Clone(); + /* + * The connectors are created using the Settings initialized in the pool. + * When connection strings are different, it will still use the first connection string which was initialized in the pool to create the connection. + * So check if connection string is same as the original connection string + * if not replace the host in the original conn string with the chosen host in the pool Settings + */ + if (!Settings.Equals(originalConnStringCopy)) + { + originalConnStringCopy.Host = Settings.Host; + Settings = originalConnStringCopy; + } + // 1. Call base logic → this increments _numConnectors and populates Connectors[] + var connector = await base.OpenNewConnector(conn, timeout, async, cancellationToken).ConfigureAwait(false); + + if (connector is not null) + { + lock (connStringToConnectorsMap) + { + if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) + { + for (var i = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[i], connector, null) == null) + break; + connStringToConnectorsMap[originalConnString] = list; + } + else + { + list = new NpgsqlConnector[MaxConnections]; + for (var i = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[i], connector, null) == null) + break; + connStringToConnectorsMap[originalConnString] = list; + } + } + + } + + return connector; + } + + internal new bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector) + + { + return base.TryGetIdleConnector(out connector); + } + + internal new void Return(NpgsqlConnector connector) + { + base.Return(connector); + } + + +} diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index 69831667cf..72079904fb 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -13,38 +13,77 @@ public class YBLoadBalancerTests : YBTestUtils int numConns = 6; [Test] - public async Task TestLoadBalance1() + public void TestLoadBalance1() { - var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=any;Timeout=0"; + var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; + var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Timeout=0;"; + List conns = new List(); - CreateCluster(); + // CreateCluster(); try { - conns = CreateConnections(connStringBuilder, numConns); - await VerifyOn("127.0.0.1", numConns/3); - await VerifyOn("127.0.0.2", numConns/3); - await VerifyOn("127.0.0.3", numConns / 3); + // for (var i = 1; i <= numConns; i++) + // { + NpgsqlConnection conn = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); + conn.Open(); + conn.Close(); + conn1.Open(); + // for (int i = 0; i < 6; i++) + // { + // NpgsqlConnection conn3 = new NpgsqlConnection(connStringBuilder1); + // conn3.Open(); + // conns.Add(conn3); + // } + conn2.Open(); + + // NpgsqlCommand cmd = new NpgsqlCommand("SELECT current_user;", conn); + // NpgsqlDataReader reader = cmd.ExecuteReader(); + // Console.WriteLine("User 1: "); + // while (reader.Read()) + // { + // Console.WriteLine("{0}", reader.GetString(0)); + // } + + NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conn1); + NpgsqlDataReader reader1 = cmd1.ExecuteReader(); + Console.WriteLine("User 1: "); + while (reader1.Read()) + { + Console.WriteLine("{0}", reader1.GetString(0)); + } + // } + NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conn2); + NpgsqlDataReader reader2 = cmd2.ExecuteReader(); + Console.WriteLine("User 2: "); + while (reader2.Read()) + { + Console.WriteLine("{0}", reader2.GetString(0)); + } + + Console.WriteLine("Connections Created"); } catch (Exception ex) { Console.WriteLine("Failure:" + ex.Message); Console.WriteLine("Failure stacktrace: " + ex.StackTrace); } - finally - { - foreach (var conn in conns) - { - conn.Close(); - } - Console.WriteLine("Verifying if all connections are closed..."); - VerifyLocal("127.0.0.1", 0); - VerifyLocal("127.0.0.2", 0); - VerifyLocal("127.0.0.3", 0); - DestroyCluster(); - } + // finally + // { + // foreach (var conn in conns) + // { + // conn.Close(); + // } + // Console.WriteLine("Verifying if all connections are closed..."); + // // VerifyLocal("127.0.0.1", 0); + // // VerifyLocal("127.0.0.2", 0); + // // VerifyLocal("127.0.0.3", 0); + // // DestroyCluster(); + // } } [Test] From ccb6facfd8070fa1e339b3d3454b3bd746e134f6 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Mon, 15 Dec 2025 10:47:25 +0530 Subject: [PATCH 02/13] Implemented TrygetidleConnector --- src/Npgsql/ClusterAwareDataSource.cs | 18 +++-- src/Npgsql/MultiHostDataSourceWrapper.cs | 3 + src/Npgsql/NpgsqlDataSource.cs | 4 ++ src/Npgsql/NpgsqlMultiHostDataSource.cs | 2 + src/Npgsql/PoolingDataSource.cs | 8 ++- src/Npgsql/UnpooledDataSource.cs | 2 + src/Npgsql/YBPoolingWrapperDataSource.cs | 92 ++++++++++++++++++++++-- 7 files changed, 114 insertions(+), 15 deletions(-) diff --git a/src/Npgsql/ClusterAwareDataSource.cs b/src/Npgsql/ClusterAwareDataSource.cs index 4bb59888de..537b0069d1 100644 --- a/src/Npgsql/ClusterAwareDataSource.cs +++ b/src/Npgsql/ClusterAwareDataSource.cs @@ -589,6 +589,8 @@ await TryGet(conn, timeoutPerHost, async, preferredType, IsOnline, poolIndex, ex internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector) => throw new NpgsqlException("Npgsql bug: trying to get an idle connector from " + nameof(ClusterAwareDataSource)); + internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, out NpgsqlConnector? connector) => throw new NotImplementedException(); + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => throw new NotImplementedException(); internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, @@ -776,16 +778,20 @@ static TargetSessionAttributes GetTargetSessionAttributes(NpgsqlConnection conne NpgsqlConnector? connector = null; try { - if (pool.TryGetIdleConnector(out connector)) + if (pool.TryGetIdleConnector(settings, out connector)) { if (databaseState == DatabaseState.Unknown) { - databaseState = await connector.QueryDatabaseState(new NpgsqlTimeout(timeoutPerHost), async, cancellationToken).ConfigureAwait(false); - Debug.Assert(databaseState != DatabaseState.Unknown); - if (!stateValidator(databaseState, preferredType)) + if (connector != null) { - pool.Return(connector); - return null; + databaseState = await connector.QueryDatabaseState(new NpgsqlTimeout(timeoutPerHost), async, cancellationToken) + .ConfigureAwait(false); + Debug.Assert(databaseState != DatabaseState.Unknown); + if (!stateValidator(databaseState, preferredType)) + { + pool.Return(connector); + return null; + } } } return connector; diff --git a/src/Npgsql/MultiHostDataSourceWrapper.cs b/src/Npgsql/MultiHostDataSourceWrapper.cs index f819d82149..7e869586f1 100644 --- a/src/Npgsql/MultiHostDataSourceWrapper.cs +++ b/src/Npgsql/MultiHostDataSourceWrapper.cs @@ -36,6 +36,9 @@ internal override ValueTask Get(NpgsqlConnection conn, NpgsqlTi => wrappedSource.Get(conn, timeout, async, cancellationToken); internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector) => throw new NpgsqlException("Npgsql bug: trying to get an idle connector from " + nameof(MultiHostDataSourceWrapper)); + + internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, out NpgsqlConnector? connector) => throw new System.NotImplementedException(); + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => throw new NpgsqlException("Npgsql bug: trying to open a new connector from " + nameof(MultiHostDataSourceWrapper)); diff --git a/src/Npgsql/NpgsqlDataSource.cs b/src/Npgsql/NpgsqlDataSource.cs index b99e4e17ee..622b6ba688 100644 --- a/src/Npgsql/NpgsqlDataSource.cs +++ b/src/Npgsql/NpgsqlDataSource.cs @@ -399,6 +399,9 @@ internal abstract ValueTask Get( internal abstract bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector); + internal abstract bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, out NpgsqlConnector? connector); + + internal abstract ValueTask OpenNewConnector( NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken); @@ -559,4 +562,5 @@ sealed class DatabaseStateInfo(DatabaseState state, NpgsqlTimeout timeout, DateT public DatabaseStateInfo() : this(default, default, default) { } } + } diff --git a/src/Npgsql/NpgsqlMultiHostDataSource.cs b/src/Npgsql/NpgsqlMultiHostDataSource.cs index 0cf350a863..49c11799bd 100644 --- a/src/Npgsql/NpgsqlMultiHostDataSource.cs +++ b/src/Npgsql/NpgsqlMultiHostDataSource.cs @@ -370,6 +370,8 @@ internal override void Return(NpgsqlConnector connector) internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector) => throw new NpgsqlException("Npgsql bug: trying to get an idle connector from " + nameof(NpgsqlMultiHostDataSource)); + internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, out NpgsqlConnector? connector) => throw new NotImplementedException(); + internal override ValueTask OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => throw new NpgsqlException("Npgsql bug: trying to open a new connector from " + nameof(NpgsqlMultiHostDataSource)); diff --git a/src/Npgsql/PoolingDataSource.cs b/src/Npgsql/PoolingDataSource.cs index accfb92ddc..732ae1af74 100644 --- a/src/Npgsql/PoolingDataSource.cs +++ b/src/Npgsql/PoolingDataSource.cs @@ -218,8 +218,10 @@ internal sealed override bool TryGetIdleConnector([NotNullWhen(true)] out Npgsql return false; } + internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, out NpgsqlConnector? connector) => throw new NotImplementedException(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool CheckIdleConnector([NotNullWhen(true)] NpgsqlConnector? connector) + protected bool CheckIdleConnector([NotNullWhen(true)] NpgsqlConnector? connector) { if (connector is null) return false; @@ -324,7 +326,7 @@ bool CheckIdleConnector([NotNullWhen(true)] NpgsqlConnector? connector) NpgsqlConnectionStringBuilder settings) => throw new NotImplementedException(); - internal sealed override void Return(NpgsqlConnector connector) + internal override void Return(NpgsqlConnector connector) { Debug.Assert(!connector.InTransaction); Debug.Assert(connector.MultiplexAsyncWritingLock == 0 || connector.IsBroken || connector.IsClosed, @@ -370,7 +372,7 @@ public override void Clear() } } - void CloseConnector(NpgsqlConnector connector) + protected void CloseConnector(NpgsqlConnector connector) { try { diff --git a/src/Npgsql/UnpooledDataSource.cs b/src/Npgsql/UnpooledDataSource.cs index 8cc2f0b9e0..a811de3b2a 100644 --- a/src/Npgsql/UnpooledDataSource.cs +++ b/src/Npgsql/UnpooledDataSource.cs @@ -39,6 +39,8 @@ internal override bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnect return false; } + internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, out NpgsqlConnector? connector) => throw new System.NotImplementedException(); + internal override ValueTask OpenNewConnector( NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) => new((NpgsqlConnector?)null); diff --git a/src/Npgsql/YBPoolingWrapperDataSource.cs b/src/Npgsql/YBPoolingWrapperDataSource.cs index d3f824101e..82310df5d4 100644 --- a/src/Npgsql/YBPoolingWrapperDataSource.cs +++ b/src/Npgsql/YBPoolingWrapperDataSource.cs @@ -1,7 +1,5 @@ using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using YBNpgsql.Internal; @@ -15,10 +13,14 @@ namespace YBNpgsql; sealed class YBPoolingWrapperDataSource: PoolingDataSource { static ConcurrentDictionary connStringToConnectorsMap = null!; + static ConcurrentDictionary connStringToIdleConnectorsMap = null!; + internal YBPoolingWrapperDataSource(NpgsqlConnectionStringBuilder settings, NpgsqlDataSourceConfiguration dataSourceConfiguration) : base(settings, dataSourceConfiguration) { connStringToConnectorsMap = new ConcurrentDictionary(); + connStringToIdleConnectorsMap = new ConcurrentDictionary(); + } internal override async ValueTask OpenNewConnector( @@ -65,15 +67,93 @@ internal YBPoolingWrapperDataSource(NpgsqlConnectionStringBuilder settings, Npgs return connector; } - internal new bool TryGetIdleConnector([NotNullWhen(true)] out NpgsqlConnector? connector) + internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder originalConnString, [NotNullWhen(true)] out NpgsqlConnector? connector) { - return base.TryGetIdleConnector(out connector); + connector = null; + + // Try to get the array for this conn string + if (!connStringToConnectorsMap.TryGetValue(originalConnString, out var connectors)) + return false; + + // Fast scan for the first non-null, *idle* connector + // (assuming "idle" means connector.State == Idle or similar) + for (var i = 0; i < connectors.Length; i++) + { + var c = connectors[i]; + if (c is null) + continue; // skip nulls quickly + + if (CheckIdleConnector(c)) + { + connector = c; + if (connStringToIdleConnectorsMap.TryGetValue(originalConnString, out var idleconnectorslist)) + { + for (var j = 0; j < MaxConnections; j++) + if (Interlocked.CompareExchange(ref idleconnectorslist[j], null, connector) == connector) + break; + connStringToIdleConnectorsMap[originalConnString] = idleconnectorslist; + + } + if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) + { + for (var j = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + break; + connStringToConnectorsMap[originalConnString] = list; + } + return true; + } + } + + return false; } - internal new void Return(NpgsqlConnector connector) + internal override void Return(NpgsqlConnector connector) { - base.Return(connector); + var flag = 0; + foreach (var connStringToConnectors in connStringToConnectorsMap) + { + for (var i = 0; i < connStringToConnectors.Value.Length; i++) + { + if (connStringToConnectors.Value[i] == null) + continue; + if (ReferenceEquals(connStringToConnectors.Value[i], connector)) + { + + if (connStringToConnectorsMap.TryGetValue(connStringToConnectors.Key, out var connectorslist)) + { + for (var j = 0; j < MaxConnections; j++) + if (Interlocked.CompareExchange(ref connectorslist[j], null, connector) == connector) + break; + connStringToConnectorsMap[connStringToConnectors.Key] = connectorslist; + + } + if (connStringToIdleConnectorsMap.TryGetValue(connStringToConnectors.Key, out var list)) + { + for (var j = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + break; + connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; + } + else + { + list = new NpgsqlConnector[MaxConnections]; + for (var j = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + break; + connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; + } + + flag = 1; + break; + } + } + if (flag == 1) + break; + } + base.Return(connector); + } From 01702f17bd73ecf05d61aa4fbffffadbaebe8112 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 16 Dec 2025 10:15:14 +0530 Subject: [PATCH 03/13] Remove static from the connStringMaps --- src/Npgsql/ClusterAwareDataSource.cs | 2 -- src/Npgsql/YBPoolingWrapperDataSource.cs | 18 +++++++----------- test/Npgsql.Tests/YBLoadBalancerTests.cs | 12 ++++++------ 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/Npgsql/ClusterAwareDataSource.cs b/src/Npgsql/ClusterAwareDataSource.cs index 537b0069d1..b7a1f73688 100644 --- a/src/Npgsql/ClusterAwareDataSource.cs +++ b/src/Npgsql/ClusterAwareDataSource.cs @@ -610,8 +610,6 @@ internal override void Return(NpgsqlConnector connector) } } UpdateConnectionMap(poolIndex, -1); - // Return the connector back to the pool to avoid memory leak - _pools[poolIndex].Return(connector); } /// diff --git a/src/Npgsql/YBPoolingWrapperDataSource.cs b/src/Npgsql/YBPoolingWrapperDataSource.cs index 82310df5d4..9c545d44d7 100644 --- a/src/Npgsql/YBPoolingWrapperDataSource.cs +++ b/src/Npgsql/YBPoolingWrapperDataSource.cs @@ -12,8 +12,8 @@ namespace YBNpgsql; /// sealed class YBPoolingWrapperDataSource: PoolingDataSource { - static ConcurrentDictionary connStringToConnectorsMap = null!; - static ConcurrentDictionary connStringToIdleConnectorsMap = null!; + ConcurrentDictionary connStringToConnectorsMap = null!; + ConcurrentDictionary connStringToIdleConnectorsMap = null!; internal YBPoolingWrapperDataSource(NpgsqlConnectionStringBuilder settings, NpgsqlDataSourceConfiguration dataSourceConfiguration) : base(settings, dataSourceConfiguration) @@ -73,7 +73,7 @@ internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder origina connector = null; // Try to get the array for this conn string - if (!connStringToConnectorsMap.TryGetValue(originalConnString, out var connectors)) + if (!connStringToIdleConnectorsMap.TryGetValue(originalConnString, out var connectors)) return false; // Fast scan for the first non-null, *idle* connector @@ -87,14 +87,10 @@ internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder origina if (CheckIdleConnector(c)) { connector = c; - if (connStringToIdleConnectorsMap.TryGetValue(originalConnString, out var idleconnectorslist)) - { - for (var j = 0; j < MaxConnections; j++) - if (Interlocked.CompareExchange(ref idleconnectorslist[j], null, connector) == connector) - break; - connStringToIdleConnectorsMap[originalConnString] = idleconnectorslist; - - } + for (var j = 0; j < MaxConnections; j++) + if (Interlocked.CompareExchange(ref connectors[j], null, connector) == connector) + break; + connStringToIdleConnectorsMap[originalConnString] = connectors; if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) { for (var j = 0; i < MaxConnections; i++) diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index 72079904fb..11127cc83b 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -32,12 +32,12 @@ public void TestLoadBalance1() conn.Open(); conn.Close(); conn1.Open(); - // for (int i = 0; i < 6; i++) - // { - // NpgsqlConnection conn3 = new NpgsqlConnection(connStringBuilder1); - // conn3.Open(); - // conns.Add(conn3); - // } + for (var i = 0; i < 6; i++) + { + NpgsqlConnection conn3 = new NpgsqlConnection(connStringBuilder1); + conn3.Open(); + conns.Add(conn3); + } conn2.Open(); // NpgsqlCommand cmd = new NpgsqlCommand("SELECT current_user;", conn); From 6b0360d891d1e460228f3607e2f40629fc156151 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 16 Dec 2025 11:24:34 +0530 Subject: [PATCH 04/13] Added tests for TopologyAware + Multithread --- src/Npgsql/TopologyAwareDataSource.cs | 2 +- src/Npgsql/YBPoolingWrapperDataSource.cs | 104 ++++----- test/Npgsql.Tests/YBLoadBalancerTests.cs | 77 ++----- test/Npgsql.Tests/YBPoolingWrapperTests.cs | 235 +++++++++++++++++++++ 4 files changed, 311 insertions(+), 107 deletions(-) create mode 100644 test/Npgsql.Tests/YBPoolingWrapperTests.cs diff --git a/src/Npgsql/TopologyAwareDataSource.cs b/src/Npgsql/TopologyAwareDataSource.cs index 0fb70ed9c7..1cd2f4df55 100644 --- a/src/Npgsql/TopologyAwareDataSource.cs +++ b/src/Npgsql/TopologyAwareDataSource.cs @@ -148,7 +148,7 @@ protected override void CreatePool(Dictionary hostsmap) var poolSettings = settings.Clone(); poolSettings.Host = host.Key; _connectionLogger.LogDebug("Adding {host} to connection pool", poolSettings.Host); - NpgsqlDataSource poolnew = settings.Pooling? new PoolingDataSource(poolSettings, dataSourceConfig): new UnpooledDataSource(poolSettings, dataSourceConfig); + NpgsqlDataSource poolnew = settings.Pooling? new YBPoolingWrapperDataSource(poolSettings, dataSourceConfig): new UnpooledDataSource(poolSettings, dataSourceConfig); _pools.Add(poolnew); int index; index = _pools.IndexOf(poolnew); diff --git a/src/Npgsql/YBPoolingWrapperDataSource.cs b/src/Npgsql/YBPoolingWrapperDataSource.cs index 9c545d44d7..5a9118cb04 100644 --- a/src/Npgsql/YBPoolingWrapperDataSource.cs +++ b/src/Npgsql/YBPoolingWrapperDataSource.cs @@ -12,6 +12,7 @@ namespace YBNpgsql; /// sealed class YBPoolingWrapperDataSource: PoolingDataSource { + static readonly object lockObject = new object(); ConcurrentDictionary connStringToConnectorsMap = null!; ConcurrentDictionary connStringToIdleConnectorsMap = null!; @@ -43,7 +44,7 @@ internal YBPoolingWrapperDataSource(NpgsqlConnectionStringBuilder settings, Npgs if (connector is not null) { - lock (connStringToConnectorsMap) + lock (lockObject) { if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) { @@ -78,27 +79,30 @@ internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder origina // Fast scan for the first non-null, *idle* connector // (assuming "idle" means connector.State == Idle or similar) - for (var i = 0; i < connectors.Length; i++) + lock (lockObject) { - var c = connectors[i]; - if (c is null) - continue; // skip nulls quickly - - if (CheckIdleConnector(c)) + for (var i = 0; i < connectors.Length; i++) { - connector = c; - for (var j = 0; j < MaxConnections; j++) - if (Interlocked.CompareExchange(ref connectors[j], null, connector) == connector) - break; - connStringToIdleConnectorsMap[originalConnString] = connectors; - if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) + var c = connectors[i]; + if (c is null) + continue; // skip nulls quickly + + if (CheckIdleConnector(c)) { - for (var j = 0; i < MaxConnections; i++) - if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + connector = c; + for (var j = 0; j < MaxConnections; j++) + if (Interlocked.CompareExchange(ref connectors[j], null, connector) == connector) break; - connStringToConnectorsMap[originalConnString] = list; + connStringToIdleConnectorsMap[originalConnString] = connectors; + if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) + { + for (var j = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + break; + connStringToConnectorsMap[originalConnString] = list; + } + return true; } - return true; } } @@ -108,46 +112,50 @@ internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder origina internal override void Return(NpgsqlConnector connector) { var flag = 0; - foreach (var connStringToConnectors in connStringToConnectorsMap) + lock (lockObject) { - for (var i = 0; i < connStringToConnectors.Value.Length; i++) + foreach (var connStringToConnectors in connStringToConnectorsMap) { - if (connStringToConnectors.Value[i] == null) - continue; - if (ReferenceEquals(connStringToConnectors.Value[i], connector)) + for (var i = 0; i < connStringToConnectors.Value.Length; i++) { - - if (connStringToConnectorsMap.TryGetValue(connStringToConnectors.Key, out var connectorslist)) + if (connStringToConnectors.Value[i] == null) + continue; + if (ReferenceEquals(connStringToConnectors.Value[i], connector)) { - for (var j = 0; j < MaxConnections; j++) - if (Interlocked.CompareExchange(ref connectorslist[j], null, connector) == connector) - break; - connStringToConnectorsMap[connStringToConnectors.Key] = connectorslist; + if (connStringToConnectorsMap.TryGetValue(connStringToConnectors.Key, out var connectorslist)) + { + for (var j = 0; j < MaxConnections; j++) + if (Interlocked.CompareExchange(ref connectorslist[j], null, connector) == connector) + break; + connStringToConnectorsMap[connStringToConnectors.Key] = connectorslist; + + } + if (connStringToIdleConnectorsMap.TryGetValue(connStringToConnectors.Key, out var list)) + { + for (var j = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + break; + connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; + } + else + { + list = new NpgsqlConnector[MaxConnections]; + for (var j = 0; i < MaxConnections; i++) + if (Interlocked.CompareExchange(ref list[j], connector, null) == null) + break; + connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; + } + + flag = 1; + break; } - if (connStringToIdleConnectorsMap.TryGetValue(connStringToConnectors.Key, out var list)) - { - for (var j = 0; i < MaxConnections; i++) - if (Interlocked.CompareExchange(ref list[j], connector, null) == null) - break; - connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; - } - else - { - list = new NpgsqlConnector[MaxConnections]; - for (var j = 0; i < MaxConnections; i++) - if (Interlocked.CompareExchange(ref list[j], connector, null) == null) - break; - connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; - } - - flag = 1; - break; } + if (flag == 1) + break; } - if (flag == 1) - break; } + base.Return(connector); } diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index 11127cc83b..69831667cf 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -13,77 +13,38 @@ public class YBLoadBalancerTests : YBTestUtils int numConns = 6; [Test] - public void TestLoadBalance1() + public async Task TestLoadBalance1() { - var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; - var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Timeout=0;"; - + var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=any;Timeout=0"; List conns = new List(); - // CreateCluster(); + CreateCluster(); try { - // for (var i = 1; i <= numConns; i++) - // { - NpgsqlConnection conn = new NpgsqlConnection(connStringBuilder1); - NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); - NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); - conn.Open(); - conn.Close(); - conn1.Open(); - for (var i = 0; i < 6; i++) - { - NpgsqlConnection conn3 = new NpgsqlConnection(connStringBuilder1); - conn3.Open(); - conns.Add(conn3); - } - conn2.Open(); - - // NpgsqlCommand cmd = new NpgsqlCommand("SELECT current_user;", conn); - // NpgsqlDataReader reader = cmd.ExecuteReader(); - // Console.WriteLine("User 1: "); - // while (reader.Read()) - // { - // Console.WriteLine("{0}", reader.GetString(0)); - // } - - NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conn1); - NpgsqlDataReader reader1 = cmd1.ExecuteReader(); - Console.WriteLine("User 1: "); - while (reader1.Read()) - { - Console.WriteLine("{0}", reader1.GetString(0)); - } - // } + conns = CreateConnections(connStringBuilder, numConns); + await VerifyOn("127.0.0.1", numConns/3); + await VerifyOn("127.0.0.2", numConns/3); + await VerifyOn("127.0.0.3", numConns / 3); - NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conn2); - NpgsqlDataReader reader2 = cmd2.ExecuteReader(); - Console.WriteLine("User 2: "); - while (reader2.Read()) - { - Console.WriteLine("{0}", reader2.GetString(0)); - } - - Console.WriteLine("Connections Created"); } catch (Exception ex) { Console.WriteLine("Failure:" + ex.Message); Console.WriteLine("Failure stacktrace: " + ex.StackTrace); } - // finally - // { - // foreach (var conn in conns) - // { - // conn.Close(); - // } - // Console.WriteLine("Verifying if all connections are closed..."); - // // VerifyLocal("127.0.0.1", 0); - // // VerifyLocal("127.0.0.2", 0); - // // VerifyLocal("127.0.0.3", 0); - // // DestroyCluster(); - // } + finally + { + foreach (var conn in conns) + { + conn.Close(); + } + Console.WriteLine("Verifying if all connections are closed..."); + VerifyLocal("127.0.0.1", 0); + VerifyLocal("127.0.0.2", 0); + VerifyLocal("127.0.0.3", 0); + DestroyCluster(); + } } [Test] diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs new file mode 100644 index 0000000000..c20870c4cb --- /dev/null +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace YBNpgsql.Tests; + +public class YBPoolingWrapperTests : YBTestUtils +{ + [Test] + public void TestPoolingForMultileConnStrings() + { + var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; + var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Timeout=0;"; + + + List conns = new List(); + CreateCluster(); + + try + { + NpgsqlConnection conn = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); + conn.Open(); + conn.Close(); + conn1.Open(); + for (var i = 0; i < 6; i++) + { + NpgsqlConnection conn3 = new NpgsqlConnection(connStringBuilder1); + conn3.Open(); + conns.Add(conn3); + } + conn2.Open(); + + NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conn1); + NpgsqlDataReader reader1 = cmd1.ExecuteReader(); + while (reader1.Read()) + { + Assert.AreEqual(reader1.GetString(0), "postgres"); + } + + NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conn2); + NpgsqlDataReader reader2 = cmd2.ExecuteReader(); + while (reader2.Read()) + { + Assert.AreEqual(reader2.GetString(0), "tester"); + } + + Console.WriteLine("Connections Created"); + } + catch (Exception ex) + { + Console.WriteLine("Failure:" + ex.Message); + Console.WriteLine("Failure stacktrace: " + ex.StackTrace); + } + finally + { + DestroyCluster(); + } + } + + [Test] + public void TestPoolingForMultileConnStringsWithTopologyKeys() + { + var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Topology Keys=cloud1.datacenter1.rack1:1;Timeout=0;"; + var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Topology Keys=cloud1.datacenter1.rack1:1;Timeout=0;"; + + + List conns = new List(); + CreateCluster(); + + try + { + NpgsqlConnection conn = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); + conn.Open(); + conn.Close(); + conn1.Open(); + for (var i = 0; i < 6; i++) + { + NpgsqlConnection conn3 = new NpgsqlConnection(connStringBuilder1); + conn3.Open(); + conns.Add(conn3); + } + conn2.Open(); + + NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conn1); + NpgsqlDataReader reader1 = cmd1.ExecuteReader(); + while (reader1.Read()) + { + Assert.AreEqual(reader1.GetString(0), "postgres"); + } + + NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conn2); + NpgsqlDataReader reader2 = cmd2.ExecuteReader(); + while (reader2.Read()) + { + Assert.AreEqual(reader2.GetString(0), "tester"); + } + + Console.WriteLine("Connections Created"); + } + catch (Exception ex) + { + Console.WriteLine("Failure:" + ex.Message); + Console.WriteLine("Failure stacktrace: " + ex.StackTrace); + } + finally + { + DestroyCluster(); + } + } + + [Test] + public void TestPoolingForMultileConnStringsMultiThread() + { + var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; + var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Timeout=0;"; + + List conns1 = new List(); + List conns2 = new List(); + + CreateCluster(); + try + { + List threads = new List(); + List conn = new List(); + var numThreads = 5; + for (var i = 0; i < numThreads; i++) + { + Thread thread = new Thread(() => { + var threadConns = CreateConnections(connStringBuilder1, 6); // Each thread uses its own list + lock (conns1) + { + conns1.AddRange(threadConns); // Safely add to the shared list + } + }); + threads.Add(thread); + } + for (var i = 0; i < numThreads; i++) + { + Thread thread = new Thread(() => { + var threadConns = CreateConnections(connStringBuilder2, 6); // Each thread uses its own list + lock (conns2) + { + conns2.AddRange(threadConns); // Safely add to the shared list + } + }); + threads.Add(thread); + } + + foreach (var thread in threads) + { + thread.Start(); + } + + foreach (var thread in threads) + { + thread.Join(); + } + NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conns1[0]); + NpgsqlDataReader reader1 = cmd1.ExecuteReader(); + while (reader1.Read()) + { + Assert.AreEqual(reader1.GetString(0), "postgres"); + } + + NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conns2[0]); + NpgsqlDataReader reader2 = cmd2.ExecuteReader(); + while (reader2.Read()) + { + Assert.AreEqual(reader2.GetString(0), "tester"); + } + + } + catch (Exception ex) + { + Console.WriteLine("Failure:" + ex.Message); + Console.WriteLine("Failure stacktrace: " + ex.StackTrace); + } + finally + { + DestroyCluster(); + } + } + static List CreateConnections(string connString, int numConns) + { + List conns = new List(); + try + { + for (var i = 1; i <= numConns; i++) + { + NpgsqlConnection conn = new NpgsqlConnection(connString); + conn.Open(); + conns.Add(conn); + } + + Console.WriteLine("Connections Created"); + } + catch (Exception ex) + { + Console.WriteLine("Failure:" + ex.Message); + Console.WriteLine("Failure stacktrace: " + ex.StackTrace); + return conns; + } + + return conns; + + } + void CreateCluster() + { + string? _Output = null; + string? _Error = null; + var cmd = "/bin/yb-ctl create --rf 3"; + ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + cmd = "bin/ysqlsh -c \"CREATE USER tester WITH PASSWORD 'abc123'\""; + ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + cmd = "bin/ysqlsh -c \"GRANT ALL PRIVILEGES ON DATABASE \"yugabyte\" to tester;\""; + ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + } + + void DestroyCluster() + { + string? _Output = null; + string? _Error = null; + var cmd = "/bin/yb-ctl destroy"; + ExecuteShellCommand(cmd, ref _Output, ref _Error ); + } +} From 0362c8c86236f8470f1ce4fdecda0b11a3d4555c Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 16 Dec 2025 21:09:39 +0530 Subject: [PATCH 05/13] Minor bug fix --- src/Npgsql/YBPoolingWrapperDataSource.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Npgsql/YBPoolingWrapperDataSource.cs b/src/Npgsql/YBPoolingWrapperDataSource.cs index 5a9118cb04..498c5b00cd 100644 --- a/src/Npgsql/YBPoolingWrapperDataSource.cs +++ b/src/Npgsql/YBPoolingWrapperDataSource.cs @@ -96,7 +96,7 @@ internal override bool TryGetIdleConnector(NpgsqlConnectionStringBuilder origina connStringToIdleConnectorsMap[originalConnString] = connectors; if (connStringToConnectorsMap.TryGetValue(originalConnString, out var list)) { - for (var j = 0; i < MaxConnections; i++) + for (var j = 0; j < MaxConnections; j++) if (Interlocked.CompareExchange(ref list[j], connector, null) == null) break; connStringToConnectorsMap[originalConnString] = list; @@ -133,7 +133,7 @@ internal override void Return(NpgsqlConnector connector) } if (connStringToIdleConnectorsMap.TryGetValue(connStringToConnectors.Key, out var list)) { - for (var j = 0; i < MaxConnections; i++) + for (var j = 0; j < MaxConnections; j++) if (Interlocked.CompareExchange(ref list[j], connector, null) == null) break; connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; @@ -141,7 +141,7 @@ internal override void Return(NpgsqlConnector connector) else { list = new NpgsqlConnector[MaxConnections]; - for (var j = 0; i < MaxConnections; i++) + for (var j = 0; j < MaxConnections; j++) if (Interlocked.CompareExchange(ref list[j], connector, null) == null) break; connStringToIdleConnectorsMap[connStringToConnectors.Key] = list; From 5e673d949987827fc49fb47693bbe721ded44bf0 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Wed, 24 Dec 2025 12:50:36 +0530 Subject: [PATCH 06/13] Changes as per review comments --- test/Npgsql.Tests/YBPoolingWrapperTests.cs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs index c20870c4cb..b047dd3972 100644 --- a/test/Npgsql.Tests/YBPoolingWrapperTests.cs +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -9,7 +9,7 @@ namespace YBNpgsql.Tests; public class YBPoolingWrapperTests : YBTestUtils { [Test] - public void TestPoolingForMultileConnStrings() + public void TestPoolingForMultipleConnStrings() { var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Timeout=0;"; @@ -57,12 +57,16 @@ public void TestPoolingForMultileConnStrings() } finally { + foreach (var conn in conns) + { + conn.Close(); + } DestroyCluster(); } } [Test] - public void TestPoolingForMultileConnStringsWithTopologyKeys() + public void TestPoolingForMultipleConnStringsWithTopologyKeys() { var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Topology Keys=cloud1.datacenter1.rack1:1;Timeout=0;"; var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Topology Keys=cloud1.datacenter1.rack1:1;Timeout=0;"; @@ -110,12 +114,16 @@ public void TestPoolingForMultileConnStringsWithTopologyKeys() } finally { + foreach (var conn in conns) + { + conn.Close(); + } DestroyCluster(); } } [Test] - public void TestPoolingForMultileConnStringsMultiThread() + public void TestPoolingForMultipleConnStringsMultiThread() { var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; var connStringBuilder2 = "host=127.0.0.1;database=yugabyte;userid=tester;password=abc123;Load Balance Hosts=any;Timeout=0;"; @@ -183,6 +191,14 @@ public void TestPoolingForMultileConnStringsMultiThread() } finally { + foreach (var conn in conns1) + { + conn.Close(); + } + foreach (var conn in conns2) + { + conn.Close(); + } DestroyCluster(); } } From a6ddf7250f23e4618c82e5dcd89e7ad57d74ec5b Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 6 Jan 2026 10:25:35 +0530 Subject: [PATCH 07/13] Removed catch to make tests fail + Updated readme --- README.md | 126 +++++++++++---------- test/Npgsql.Tests/YBPoolingWrapperTests.cs | 15 --- 2 files changed, 64 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index f9fa98fdac..d56d910042 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,4 @@ -# Npgsql - the .NET data provider for PostgreSQL - -[![stable](https://img.shields.io/nuget/v/Npgsql.svg?label=stable)](https://www.nuget.org/packages/Npgsql/) -[![next patch](https://img.shields.io/myget/npgsql/v/npgsql.svg?label=next%20patch)](https://www.myget.org/feed/npgsql/package/nuget/Npgsql) -[![daily builds (vnext)](https://img.shields.io/myget/npgsql-vnext/v/npgsql.svg?label=vnext)](https://www.myget.org/feed/npgsql-vnext/package/nuget/Npgsql) -[![build](https://github.com/npgsql/npgsql/actions/workflows/build.yml/badge.svg)](https://github.com/npgsql/npgsql/actions/workflows/build.yml) -[![gitter](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/npgsql/npgsql) - -## What is Npgsql? - -Npgsql is the open source .NET data provider for PostgreSQL. It allows you to connect and interact with PostgreSQL server using .NET. - -For the full documentation, please visit [the Npgsql website](https://www.npgsql.org). For the Entity Framework Core provider that works with this provider, see [Npgsql.EntityFrameworkCore.PostgreSQL](https://github.com/npgsql/efcore.pg). - -## Quickstart - -Here's a basic code snippet to get you started: - -```csharp -using Npgsql; - -var connString = "Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase"; - -var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString); -var dataSource = dataSourceBuilder.Build(); - -var conn = await dataSource.OpenConnectionAsync(); - -// Insert some data -await using (var cmd = new NpgsqlCommand("INSERT INTO data (some_field) VALUES (@p)", conn)) -{ - cmd.Parameters.AddWithValue("p", "Hello world"); - await cmd.ExecuteNonQueryAsync(); -} - -// Retrieve all rows -await using (var cmd = new NpgsqlCommand("SELECT some_field FROM data", conn)) -await using (var reader = await cmd.ExecuteReaderAsync()) -{ - while (await reader.ReadAsync()) - Console.WriteLine(reader.GetString(0)); -} -``` - -## Key features - -* High-performance PostgreSQL driver. Regularly figures in the top contenders on the [TechEmpower Web Framework Benchmarks](https://www.techempower.com/benchmarks/). -* Full support of most PostgreSQL types, including advanced ones such as arrays, enums, ranges, multiranges, composites, JSON, PostGIS and others. -* Highly-efficient bulk import/export API. -* Failover, load balancing and general multi-host support. -* Great integration with Entity Framework Core via [Npgsql.EntityFrameworkCore.PostgreSQL](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL). - -For the full documentation, please visit the Npgsql website at [https://www.npgsql.org](https://www.npgsql.org). - -## YugabyteDB Npgsql Features +# Npgsql YugabyteDB - the .NET data provider for YugabyteDB Yugabyte Npgsql driver is a distributed .NET driver for YSQL built on the PostgreSQL Npgsql driver. Although the upstream PostgreSQL driver works with YugabyteDB, the Yugabyte driver enhances YugabyteDB by eliminating the need for external load balancers. @@ -79,12 +25,12 @@ Load balancing connection properties: The following connection properties are added to enable load balancing: * Load Balance Hosts - Starting with version 8.0.3.2, it expects one of False, Any (same as true), OnlyPrimary, OnlyRR, PreferPrimary and PreferRR as its possible values. - * False - No connection load balancing. Behaviour is similar to vanilla Npgsql driver - * Any - Same as value true. Distribute connections equally across all nodes in the cluster, irrespective of its type (primary or read-replica) - * OnlyPrimary - Create connections equally across only the primary nodes of the cluster - * OnlyRR - Create connections equally across only the read-replica nodes of the cluster - * PreferPrimary - Create connections equally across primary cluster nodes. If none available, on any available read replica node in the cluster - * PreferRR - Create connections equally across read replica nodes of the cluster. If none available, on any available primary cluster node + * False - No connection load balancing. Behaviour is similar to vanilla Npgsql driver + * Any - Same as value true. Distribute connections equally across all nodes in the cluster, irrespective of its type (primary or read-replica) + * OnlyPrimary - Create connections equally across only the primary nodes of the cluster + * OnlyRR - Create connections equally across only the read-replica nodes of the cluster + * PreferPrimary - Create connections equally across primary cluster nodes. If none available, on any available read replica node in the cluster + * PreferRR - Create connections equally across read replica nodes of the cluster. If none available, on any available primary cluster node * Topology Keys - provide comma-separated geo-location values to enable topology-aware load balancing. Geo-locations can be provided as cloud.region.zone. * YB Servers Refresh Interval - The list of servers, to balance the connection load on, are refreshed periodically every 5 minutes by default. This time can be regulated by this property. * Fallback To Topology Keys Only - Decides if the driver can fall back to nodes outside of the given placements for new connections, if the nodes in the given placements are not available. Value true means stick to explicitly given placements for fallback, else fail. Value false means fall back to entire cluster nodes when nodes in the given placements are unavailable. Default is false. It is ignored if topology-keys is not specified or load-balance is set to either prefer-primary or prefer-rr. @@ -116,4 +62,60 @@ Multiple topologies can also be passed to the Topology Keys property, and each o ```csharp var connString = "host=127.0.0.3;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;Timeout=0;Topology Keys=cloud1.region1.zone1:1,cloud2.region2.zone2:2"; -``` \ No newline at end of file +``` + +### ----------------------------------- Upstream ReadMe Follows -------------------------------------- + +# Npgsql - the .NET data provider for PostgreSQL + +[![stable](https://img.shields.io/nuget/v/Npgsql.svg?label=stable)](https://www.nuget.org/packages/Npgsql/) +[![next patch](https://img.shields.io/myget/npgsql/v/npgsql.svg?label=next%20patch)](https://www.myget.org/feed/npgsql/package/nuget/Npgsql) +[![daily builds (vnext)](https://img.shields.io/myget/npgsql-vnext/v/npgsql.svg?label=vnext)](https://www.myget.org/feed/npgsql-vnext/package/nuget/Npgsql) +[![build](https://github.com/npgsql/npgsql/actions/workflows/build.yml/badge.svg)](https://github.com/npgsql/npgsql/actions/workflows/build.yml) +[![gitter](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/npgsql/npgsql) + +## What is Npgsql? + +Npgsql is the open source .NET data provider for PostgreSQL. It allows you to connect and interact with PostgreSQL server using .NET. + +For the full documentation, please visit [the Npgsql website](https://www.npgsql.org). For the Entity Framework Core provider that works with this provider, see [Npgsql.EntityFrameworkCore.PostgreSQL](https://github.com/npgsql/efcore.pg). + +## Quickstart + +Here's a basic code snippet to get you started: + +```csharp +using Npgsql; + +var connString = "Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase"; + +var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString); +var dataSource = dataSourceBuilder.Build(); + +var conn = await dataSource.OpenConnectionAsync(); + +// Insert some data +await using (var cmd = new NpgsqlCommand("INSERT INTO data (some_field) VALUES (@p)", conn)) +{ + cmd.Parameters.AddWithValue("p", "Hello world"); + await cmd.ExecuteNonQueryAsync(); +} + +// Retrieve all rows +await using (var cmd = new NpgsqlCommand("SELECT some_field FROM data", conn)) +await using (var reader = await cmd.ExecuteReaderAsync()) +{ + while (await reader.ReadAsync()) + Console.WriteLine(reader.GetString(0)); +} +``` + +## Key features + +* High-performance PostgreSQL driver. Regularly figures in the top contenders on the [TechEmpower Web Framework Benchmarks](https://www.techempower.com/benchmarks/). +* Full support of most PostgreSQL types, including advanced ones such as arrays, enums, ranges, multiranges, composites, JSON, PostGIS and others. +* Highly-efficient bulk import/export API. +* Failover, load balancing and general multi-host support. +* Great integration with Entity Framework Core via [Npgsql.EntityFrameworkCore.PostgreSQL](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL). + +For the full documentation, please visit the Npgsql website at [https://www.npgsql.org](https://www.npgsql.org). \ No newline at end of file diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs index b047dd3972..0ad51ee624 100644 --- a/test/Npgsql.Tests/YBPoolingWrapperTests.cs +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -50,11 +50,6 @@ public void TestPoolingForMultipleConnStrings() Console.WriteLine("Connections Created"); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { foreach (var conn in conns) @@ -107,11 +102,6 @@ public void TestPoolingForMultipleConnStringsWithTopologyKeys() Console.WriteLine("Connections Created"); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { foreach (var conn in conns) @@ -184,11 +174,6 @@ public void TestPoolingForMultipleConnStringsMultiThread() } } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { foreach (var conn in conns1) From 058abf8ca992f1b065f36d80cff29d11d4e11507 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 6 Jan 2026 10:29:40 +0530 Subject: [PATCH 08/13] Removed whitespace --- src/Npgsql/PublicAPI.Unshipped.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Npgsql/PublicAPI.Unshipped.txt b/src/Npgsql/PublicAPI.Unshipped.txt index 8d8af046d1..8d6987bb4b 100644 --- a/src/Npgsql/PublicAPI.Unshipped.txt +++ b/src/Npgsql/PublicAPI.Unshipped.txt @@ -76,4 +76,3 @@ YBNpgsql.NpgsqlConnection.ReloadTypesAsync(System.Threading.CancellationToken ca *REMOVED*YBNpgsql.NpgsqlSlimDataSourceBuilder.MapComposite(string? pgName = null, YBNpgsql.INpgsqlNameTranslator? nameTranslator = null) -> YBNpgsql.TypeMapping.INpgsqlTypeMapper! *REMOVED*YBNpgsql.NpgsqlSlimDataSourceBuilder.MapEnum(System.Type! clrType, string? pgName = null, YBNpgsql.INpgsqlNameTranslator? nameTranslator = null) -> YBNpgsql.TypeMapping.INpgsqlTypeMapper! *REMOVED*YBNpgsql.NpgsqlSlimDataSourceBuilder.MapEnum(string? pgName = null, YBNpgsql.INpgsqlNameTranslator? nameTranslator = null) -> YBNpgsql.TypeMapping.INpgsqlTypeMapper! - From 2d43b7ec787aae4c21f2b9cd7c9db2694608f38f Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 13 Jan 2026 10:39:09 +0530 Subject: [PATCH 09/13] Added lock to the code for settings change for thread safety + changes made as per review comments --- src/Npgsql/YBPoolingWrapperDataSource.cs | 32 +++-- .../YBClusterAwareRRSupportTests.cs | 36 +----- test/Npgsql.Tests/YBLoadBalancerTests.cs | 18 +-- test/Npgsql.Tests/YBPoolingWrapperTests.cs | 50 +++++--- test/Npgsql.Tests/YBPreparedStatementsTest.cs | 4 +- test/Npgsql.Tests/YBTestUtils.cs | 4 + .../YBTopologyAwareRRSupportTests.cs | 117 +++--------------- 7 files changed, 82 insertions(+), 179 deletions(-) diff --git a/src/Npgsql/YBPoolingWrapperDataSource.cs b/src/Npgsql/YBPoolingWrapperDataSource.cs index 498c5b00cd..ce03806dfc 100644 --- a/src/Npgsql/YBPoolingWrapperDataSource.cs +++ b/src/Npgsql/YBPoolingWrapperDataSource.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using YBNpgsql.Internal; @@ -27,20 +28,27 @@ internal YBPoolingWrapperDataSource(NpgsqlConnectionStringBuilder settings, Npgs internal override async ValueTask OpenNewConnector( NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken, NpgsqlConnectionStringBuilder originalConnString) { - var originalConnStringCopy = originalConnString.Clone(); - /* - * The connectors are created using the Settings initialized in the pool. - * When connection strings are different, it will still use the first connection string which was initialized in the pool to create the connection. - * So check if connection string is same as the original connection string - * if not replace the host in the original conn string with the chosen host in the pool Settings - */ - if (!Settings.Equals(originalConnStringCopy)) + ConfiguredValueTaskAwaitable awaitableconnector; + NpgsqlConnector? connector = null; + lock (lockObject) { - originalConnStringCopy.Host = Settings.Host; - Settings = originalConnStringCopy; + var originalConnStringCopy = originalConnString.Clone(); + /* + * The connectors are created using the Settings initialized in the pool. + * When connection strings are different, it will still use the first connection string which was initialized in the pool to create the connection. + * So check if connection string is same as the original connection string + * if not replace the host in the original conn string with the chosen host in the pool Settings + */ + if (!Settings.Equals(originalConnStringCopy)) + { + originalConnStringCopy.Host = Settings.Host; + Settings = originalConnStringCopy; + } + // 1. Call base logic → this increments _numConnectors and populates Connectors[] + awaitableconnector = base.OpenNewConnector(conn, timeout, async, cancellationToken).ConfigureAwait(false); } - // 1. Call base logic → this increments _numConnectors and populates Connectors[] - var connector = await base.OpenNewConnector(conn, timeout, async, cancellationToken).ConfigureAwait(false); + + connector = await awaitableconnector; if (connector is not null) { diff --git a/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs b/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs index 3b40e9f2fe..f214f21c89 100644 --- a/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs @@ -23,11 +23,6 @@ public async Task TestOnlyPrimary() conns = await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -47,11 +42,6 @@ public async Task TestPreferPrimary() conns = await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -83,11 +73,6 @@ public async Task TestPreferPrimaryAllNodesDown() conns = await CreateConnections(connStringBuilder, numConns, new []{-1, -1, -1, numConns / 3, numConns / 3, numConns / 3}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -106,11 +91,7 @@ public async Task TestOnlyRR() conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, numConns / 3, numConns / 3, numConns / 3}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -130,11 +111,6 @@ public async Task TestPreferRR() conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, numConns / 3, numConns / 3, numConns / 3}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -166,11 +142,6 @@ public async Task TestPreferRRAllNodesDown() conns = await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, -1, -1, -1}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -189,11 +160,6 @@ public async Task TestAny() conns = await CreateConnections(connStringBuilder, numConns, new []{numConns / 6, numConns / 6, numConns / 6, numConns / 6, numConns / 6, numConns / 6}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index 69831667cf..c0d7350a82 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -28,11 +28,7 @@ public async Task TestLoadBalance1() await VerifyOn("127.0.0.3", numConns / 3); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { foreach (var conn in conns) @@ -73,11 +69,7 @@ public async Task TestLoadBalance2() await VerifyOn("127.0.0.2", 5); await VerifyOn("127.0.0.3", 5); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { foreach (var conn in conns) @@ -130,11 +122,7 @@ public async Task TestLoadBalance3() await VerifyOn("127.0.0.2", numThreads * numConns/3); await VerifyOn("127.0.0.3", numThreads * numConns / 3); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { Console.WriteLine("Conns count" + allConns.Count); diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs index 0ad51ee624..8f7a415078 100644 --- a/test/Npgsql.Tests/YBPoolingWrapperTests.cs +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -18,11 +18,12 @@ public void TestPoolingForMultipleConnStrings() List conns = new List(); CreateCluster(); + NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); + try { NpgsqlConnection conn = new NpgsqlConnection(connStringBuilder1); - NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); - NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); conn.Open(); conn.Close(); conn1.Open(); @@ -56,6 +57,9 @@ public void TestPoolingForMultipleConnStrings() { conn.Close(); } + + conn1.Close(); + conn2.Close(); DestroyCluster(); } } @@ -70,11 +74,12 @@ public void TestPoolingForMultipleConnStringsWithTopologyKeys() List conns = new List(); CreateCluster(); + NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); + NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); + try { NpgsqlConnection conn = new NpgsqlConnection(connStringBuilder1); - NpgsqlConnection conn1 = new NpgsqlConnection(connStringBuilder1); - NpgsqlConnection conn2 = new NpgsqlConnection(connStringBuilder2); conn.Open(); conn.Close(); conn1.Open(); @@ -108,6 +113,9 @@ public void TestPoolingForMultipleConnStringsWithTopologyKeys() { conn.Close(); } + + conn1.Close(); + conn2.Close(); DestroyCluster(); } } @@ -133,7 +141,7 @@ public void TestPoolingForMultipleConnStringsMultiThread() var threadConns = CreateConnections(connStringBuilder1, 6); // Each thread uses its own list lock (conns1) { - conns1.AddRange(threadConns); // Safely add to the shared list + conns1.AddRange(threadConns); } }); threads.Add(thread); @@ -159,18 +167,25 @@ public void TestPoolingForMultipleConnStringsMultiThread() { thread.Join(); } - NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conns1[0]); - NpgsqlDataReader reader1 = cmd1.ExecuteReader(); - while (reader1.Read()) + + foreach (var conn1 in conns1) { - Assert.AreEqual(reader1.GetString(0), "postgres"); + NpgsqlCommand cmd1 = new NpgsqlCommand("SELECT current_user;", conn1); + NpgsqlDataReader reader1 = cmd1.ExecuteReader(); + while (reader1.Read()) + { + Assert.AreEqual("postgres", reader1.GetString(0)); + } } - NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conns2[0]); - NpgsqlDataReader reader2 = cmd2.ExecuteReader(); - while (reader2.Read()) + foreach (var conn2 in conns2) { - Assert.AreEqual(reader2.GetString(0), "tester"); + NpgsqlCommand cmd2 = new NpgsqlCommand("SELECT current_user;", conn2); + NpgsqlDataReader reader2 = cmd2.ExecuteReader(); + while (reader2.Read()) + { + Assert.AreEqual("tester",reader2.GetString(0) ); + } } } @@ -218,12 +233,15 @@ void CreateCluster() var cmd = "/bin/yb-ctl create --rf 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - cmd = "bin/ysqlsh -c \"CREATE USER tester WITH PASSWORD 'abc123'\""; + Console.WriteLine("Error:" + _Error); + cmd = "/bin/ysqlsh -c \"CREATE USER tester WITH PASSWORD 'abc123'\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - cmd = "bin/ysqlsh -c \"GRANT ALL PRIVILEGES ON DATABASE \"yugabyte\" to tester;\""; + Console.WriteLine("Error:" + _Error); + cmd = "/bin/ysqlsh -c \"GRANT ALL PRIVILEGES ON DATABASE \"yugabyte\" to tester;\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + Console.WriteLine("Error:" + _Error); } void DestroyCluster() @@ -232,5 +250,7 @@ void DestroyCluster() string? _Error = null; var cmd = "/bin/yb-ctl destroy"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + Console.WriteLine("Error:" + _Error); } } diff --git a/test/Npgsql.Tests/YBPreparedStatementsTest.cs b/test/Npgsql.Tests/YBPreparedStatementsTest.cs index d9e8a245bf..050749d35a 100644 --- a/test/Npgsql.Tests/YBPreparedStatementsTest.cs +++ b/test/Npgsql.Tests/YBPreparedStatementsTest.cs @@ -36,9 +36,9 @@ public async Task PreparedStatementsTestWithFlagsEnabled() conn.Close(); } - catch (PostgresException e) + finally { - Console.WriteLine(e); + } } diff --git a/test/Npgsql.Tests/YBTestUtils.cs b/test/Npgsql.Tests/YBTestUtils.cs index 72847107ab..c339fb7d44 100644 --- a/test/Npgsql.Tests/YBTestUtils.cs +++ b/test/Npgsql.Tests/YBTestUtils.cs @@ -13,6 +13,10 @@ public class YBTestUtils public void ExecuteShellCommand(string argument, ref string? _outputMessage, ref string? _errorMessage) { var path = Environment.GetEnvironmentVariable("YBDB_PATH"); + if (path == null) + { + throw new ArgumentException("YBDB_PATH not initialized"); + } var arguments = path + argument; // Set process variable // Provides access to local and remote processes and enables you to start and stop local system processes. diff --git a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs index 9c942dbd43..eb3968d405 100644 --- a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs @@ -23,11 +23,6 @@ public async Task TestOnlyPrimary() conns = await CreateConnections(connStringBuilder, numConns, new []{0, numConns, 0, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -56,11 +51,6 @@ public async Task TestOnlyPrimaryAllNodesDownAllPlacement() conns = await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } finally { CloseConnections(conns); @@ -94,11 +84,6 @@ public void TestOnlyPrimaryAllNodesDownInAllPlacementsFallBackToTopologyOnly() } } - catch (NpgsqlException ex) - { - if (ex.Message.Equals("No suitable host was found", StringComparison.OrdinalIgnoreCase)) - Console.WriteLine("Expected Failure:" + ex.Message); - } finally { CloseConnections(conns); @@ -124,11 +109,7 @@ public async Task TestOnlyPrimaryAllNodesDownPrimarylacement() conns = await CreateConnections(connStringBuilder, numConns, new []{0, -1, numConns, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -148,11 +129,7 @@ public async Task TestPreferPrimary() conns = await CreateConnections(connStringBuilder, numConns, new []{0, numConns, 0, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -177,11 +154,7 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, -1, numConns, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -222,11 +195,7 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() conns.Concat(await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, numConns / 3, numConns / 3, numConns / 3})); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -255,11 +224,7 @@ public async Task TestPreferPrimaryAllNodesDownAllPlacement() conns = await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -277,11 +242,7 @@ public async Task TestOnlyRR() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, numConns, 0, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -306,11 +267,7 @@ public async Task TestOnlyRRAllNodesDownInPrimaryPlacement() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, -1, numConns, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -338,11 +295,7 @@ public async Task TestOnlyRRAllNodesDownInAllPlacement() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, -1, -1, numConns}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -376,11 +329,7 @@ public void TestOnlyRRAllNodesDownInAllPlacementsFallBackToTopologyOnly() } } - catch (NpgsqlException ex) - { - if (ex.Message.Equals("No suitable host was found", StringComparison.OrdinalIgnoreCase)) - Console.WriteLine("Expected Failure:" + ex.Message); - } + finally { CloseConnections(conns); @@ -399,11 +348,7 @@ public async Task TestPreferRR() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, numConns, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -428,11 +373,7 @@ public async Task TestPreferRRAllNodesDownInPrimaryPlacement() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, -1, numConns, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -460,11 +401,7 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, -1, -1, numConns}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -503,11 +440,7 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() conns.Concat(await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, numConns, -1, -1})); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -525,11 +458,7 @@ public async Task TestAny() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, numConns / 2, 0, numConns / 2, 0, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -557,11 +486,7 @@ public async Task TestAnyAllNodesDownPrimaryPlacement() { conns = await CreateConnections(connStringBuilder, numConns, new []{0, -1, numConns /2, -1, numConns / 2, 0}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); @@ -601,11 +526,7 @@ public void TestAnyAllNodesDownAllPlacementFallBackToTopologyOnly() } } - catch (NpgsqlException ex) - { - if (ex.Message.Equals("No suitable host was found", StringComparison.OrdinalIgnoreCase)) - Console.WriteLine("Expected Failure:" + ex.Message); - } + finally { CloseConnections(conns); @@ -639,11 +560,7 @@ public async Task TestAnyAllNodesDownAllPlacement() { conns = await CreateConnections(connStringBuilder, numConns, new []{numConns /2 , -1, -1, -1, -1, numConns / 2}); } - catch (Exception ex) - { - Console.WriteLine("Failure:" + ex.Message); - Console.WriteLine("Failure stacktrace: " + ex.StackTrace); - } + finally { CloseConnections(conns); From 1847ffbdb037d6b91372bd7e86196ebde8ca7e5b Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 20 Jan 2026 13:37:16 +0530 Subject: [PATCH 10/13] Fixed the fallback bug + Minor changes to tests --- src/Npgsql/ClusterAwareDataSource.cs | 128 +++++++++++++++--- src/Npgsql/TopologyAwareDataSource.cs | 22 ++- .../YBClusterAwareRRSupportTests.cs | 28 +++- .../YBFallBackTopologyExtended.cs | 104 +++++++++++++- test/Npgsql.Tests/YBFallbackTopolgyTests.cs | 22 ++- test/Npgsql.Tests/YBLoadBalancerTests.cs | 16 ++- test/Npgsql.Tests/YBPoolingWrapperTests.cs | 23 ++-- .../YBTopologyAwareRRSupportTests.cs | 98 +++++++++++++- 8 files changed, 390 insertions(+), 51 deletions(-) diff --git a/src/Npgsql/ClusterAwareDataSource.cs b/src/Npgsql/ClusterAwareDataSource.cs index b7a1f73688..d2d3fd86bf 100644 --- a/src/Npgsql/ClusterAwareDataSource.cs +++ b/src/Npgsql/ClusterAwareDataSource.cs @@ -78,7 +78,12 @@ public class ClusterAwareDataSource: NpgsqlDataSource /// /// Stores a map of host to their priority /// - protected static Dictionary priorityToPoolIndexMap = new Dictionary(); + protected static Dictionary> priorityToPoolIndexMapPrimary = new Dictionary>(); + + /// + /// Stores a map of host to their priority + /// + protected static Dictionary> priorityToPoolIndexMapRR = new Dictionary>(); /// /// Connection settings @@ -130,6 +135,19 @@ public class ClusterAwareDataSource: NpgsqlDataSource /// protected ConcurrentDictionary> fallbackPublicIPs; + /// + /// Contains a dictionary of IPs for all Read replica IPs + /// Key = Read replica IPs + /// Value = Dictionary (IP , nodeType) + /// + protected Dictionary AllRRIps = new Dictionary(); + /// + /// Contains a dictionary of IPs for PRimary nodes of cluster + /// Key = Primary node IPs + /// Value = Dictionary (IP , nodeType) + /// + protected Dictionary AllPrimaryIps = new Dictionary(); + /// /// To set refresh value explicitly /// @@ -343,7 +361,7 @@ void UpdateConnectionMap(int poolIndex, int incDec) if (poolToNumConnMapPrimary.ContainsKey(currPool)) { currentCount = poolToNumConnMapPrimary[currPool]; - poolToNumConnMapPrimary[currPool] += incDec; + poolToNumConnMapPrimary[currPool] += incDec; _connectionLogger.LogTrace("Updated the current count for {host} from {currentCount} to {newCount}", _pools[poolIndex].Settings.Host, currentCount, poolToNumConnMapPrimary[currPool]); } @@ -406,7 +424,7 @@ internal override async ValueTask Get(NpgsqlConnection conn, Np { NpgsqlConnector? connector = null; var exceptions = new List(); - connector = await getConnector(conn, timeout,async, cancellationToken, exceptions).ConfigureAwait(false); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, false).ConfigureAwait(false); if (this is TopologyAwareDataSource) { @@ -424,7 +442,7 @@ internal override async ValueTask Get(NpgsqlConnection conn, Np { exceptions.Clear(); CreatePool(fallbackPrivateIPs[i]); - connector = await getConnector(conn, timeout,async, cancellationToken, exceptions).ConfigureAwait(false); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, false).ConfigureAwait(false); if (connector != null) break; } @@ -432,7 +450,7 @@ internal override async ValueTask Get(NpgsqlConnection conn, Np { exceptions.Clear(); CreatePool(fallbackPublicIPs[i]); - connector = await getConnector(conn, timeout,async, cancellationToken, exceptions).ConfigureAwait(false); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, false).ConfigureAwait(false); if (connector != null) break; } @@ -449,24 +467,38 @@ internal override async ValueTask Get(NpgsqlConnection conn, Np { exceptions.Clear(); CreatePool(fallbackPrivateIPs[REST_OF_CLUSTER]); - connector = await getConnector(conn, timeout,async, cancellationToken, exceptions).ConfigureAwait(false); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, false).ConfigureAwait(false); } else if (public_ip != null) { exceptions.Clear(); CreatePool(fallbackPublicIPs[REST_OF_CLUSTER]); - connector = await getConnector(conn, timeout,async, cancellationToken, exceptions).ConfigureAwait(false); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, false).ConfigureAwait(false); } } } + if (connector == null) + { + if (Settings.LoadBalanceHosts == LoadBalanceHosts.PreferPrimary) + { + CreatePool(AllRRIps); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, true).ConfigureAwait(false); + } + if (Settings.LoadBalanceHosts == LoadBalanceHosts.PreferRR) + { + CreatePool(AllPrimaryIps); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, true).ConfigureAwait(false); + } + } + if (connector != null) { return connector; } _connectionLogger.LogDebug("Failed to apply Load balance. Trying normal connection"); conn.Settings.LoadBalanceHosts = LoadBalanceHosts.False; - connector = await getConnector(conn, timeout,async, cancellationToken, exceptions).ConfigureAwait(false); + connector = await getConnector(conn, timeout,async, cancellationToken, exceptions, true).ConfigureAwait(false); return connector ?? throw NoSuitableHostsException(exceptions); } @@ -476,15 +508,36 @@ internal override async ValueTask Get(NpgsqlConnection conn, Np if (priority == chosenHostPriority) return null; NpgsqlConnector? connector = null; - var poolIndex = -1; - if (priorityToPoolIndexMap.ContainsKey(priority)) + Dictionary>? priorityToPoolIndexMap = null; + if (Settings.LoadBalanceHosts == LoadBalanceHosts.OnlyPrimary || Settings.LoadBalanceHosts == LoadBalanceHosts.PreferPrimary) + { + priorityToPoolIndexMap = priorityToPoolIndexMapPrimary; + } + else if (Settings.LoadBalanceHosts == LoadBalanceHosts.OnlyRR || Settings.LoadBalanceHosts == LoadBalanceHosts.PreferRR) { - poolIndex = priorityToPoolIndexMap[priority]; + priorityToPoolIndexMap = priorityToPoolIndexMapRR; + } + else if (Settings.LoadBalanceHosts == LoadBalanceHosts.Any || Settings.LoadBalanceHosts == LoadBalanceHosts.True) + { + priorityToPoolIndexMap = priorityToPoolIndexMapPrimary + .Concat(priorityToPoolIndexMapRR) + .GroupBy(p => p.Key) + .ToDictionary( + g => g.Key, + g => g.SelectMany(x => x.Value).ToList() + ); + } + Debug.Assert(priorityToPoolIndexMap != null, nameof(priorityToPoolIndexMap) + " = null"); + if (!priorityToPoolIndexMap.TryGetValue(priority, out var poolIndices)){ + priority++; + connector = await getConnector(chosenHostPriority, priority, conn, timeout, async, cancellationToken, exceptions) + .ConfigureAwait(false); + return connector; } - if (poolIndex == -1) - return null; - UpdateConnectionMap(poolIndex, 1); + foreach (var poolIndex in poolIndices) + { + UpdateConnectionMap(poolIndex, 1); var timeoutPerHost = timeout.IsSet ? timeout.CheckAndGetTimeLeft() : TimeSpan.Zero; var preferredType = GetTargetSessionAttributes(conn); var checkUnpreferred = preferredType is TargetSessionAttributes.PreferPrimary or TargetSessionAttributes.PreferStandby; @@ -498,10 +551,16 @@ await TryGet(conn, timeoutPerHost, async, preferredType, IsOnline, poolIndex, ex : null); if (connector == null) { + unreachableHostsIndices.Add(poolIndex); + var settingsHost = _pools[poolIndex].Settings.Host; + if (settingsHost != null) unreachableHosts.Add(settingsHost); + var pool = _pools[poolIndex]; + if (poolToNumConnMapPrimary.ContainsKey(pool)) + poolToNumConnMapPrimary.Remove(pool); + else if (poolToNumConnMapRR.ContainsKey(pool)) + poolToNumConnMapRR.Remove(pool); UpdateConnectionMap(poolIndex, -1); - priority++; - connector = await getConnector(chosenHostPriority, priority, conn, timeout, async, cancellationToken, exceptions) - .ConfigureAwait(false); + continue; } if (connector != null) @@ -511,18 +570,28 @@ await TryGet(conn, timeoutPerHost, async, preferredType, IsOnline, poolIndex, ex { unreachableHosts.Remove(host); } + + return connector; + } } + + priority++; + connector = await getConnector(chosenHostPriority, priority, conn, timeout, async, cancellationToken, exceptions) + .ConfigureAwait(false); return connector; } async Task getConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, - CancellationToken cancellationToken, List exceptions) + CancellationToken cancellationToken, List exceptions, bool isFinalFallback) { NpgsqlConnector? connector = null; - for (var i = 0; i < _pools.Count; i++) + while (true) { CheckDisposed(); - + if (AreAllEligiblePoolsExhausted() && !isFinalFallback) + { + return null; + } var poolIndex = conn.Settings.LoadBalanceHosts != LoadBalanceHosts.False ? GetRoundRobinIndex() : -2; if (poolIndex == -1) return null; @@ -719,6 +788,25 @@ int GetRoundRobinIndex() } } + bool AreAllEligiblePoolsExhausted() + { + return settings.LoadBalanceHosts switch + { + LoadBalanceHosts.OnlyPrimary or LoadBalanceHosts.PreferPrimary=> + poolToNumConnMapPrimary.Count == 0, + + LoadBalanceHosts.OnlyRR or LoadBalanceHosts.PreferRR => + poolToNumConnMapRR.Count == 0, + + LoadBalanceHosts.Any or LoadBalanceHosts.True => + poolToNumConnMapPrimary.Count == 0 && + poolToNumConnMapRR.Count == 0, + + _ => true + }; + } + + int getHosts(Dictionary poolToNumConnMap) { var PoolIndex = -1; diff --git a/src/Npgsql/TopologyAwareDataSource.cs b/src/Npgsql/TopologyAwareDataSource.cs index 1cd2f4df55..5c88d922ac 100644 --- a/src/Npgsql/TopologyAwareDataSource.cs +++ b/src/Npgsql/TopologyAwareDataSource.cs @@ -16,13 +16,11 @@ namespace YBNpgsql; public sealed class TopologyAwareDataSource: ClusterAwareDataSource { ConcurrentDictionary?> allowedPlacements; - Dictionary AllRRIps = new Dictionary(); - Dictionary AllPrimaryIps = new Dictionary(); internal TopologyAwareDataSource(NpgsqlConnectionStringBuilder settings, NpgsqlDataSourceConfiguration dataSourceConfig) : base(settings,dataSourceConfig,false) { allowedPlacements = new ConcurrentDictionary?>(); - ParseGeoLocations(); + ParseGeoLocations(); _connectionLogger.LogDebug("Allowed Placements: {allowedPlacements}", allowedPlacements); Debug.Assert(initialHosts != null, nameof(initialHosts) + " != null"); foreach (var host in initialHosts.ToList()) @@ -153,13 +151,26 @@ protected override void CreatePool(Dictionary hostsmap) int index; index = _pools.IndexOf(poolnew); var priority = hostToPriorityMap[host.Key]; - priorityToPoolIndexMap[priority] = index; if (host.Value.Equals("primary", StringComparison.OrdinalIgnoreCase)) { + if (!priorityToPoolIndexMapPrimary.TryGetValue(priority, out var list)) + { + list = new List(); + priorityToPoolIndexMapPrimary[priority] = list; + } + + list.Add(index);; poolToNumConnMapPrimary[poolnew] = 0; } else if (host.Value.Equals("read_replica", StringComparison.OrdinalIgnoreCase)) { + if (!priorityToPoolIndexMapRR.TryGetValue(priority, out var list)) + { + list = new List(); + priorityToPoolIndexMapRR[priority] = list; + } + + list.Add(index); poolToNumConnMapRR[poolnew] = 0; } } @@ -280,7 +291,7 @@ Dictionary GetRelevantServerToNodeTypeMap(Dictionary GetPrivateOrPublicServers(Dictionary privateHosts, Dictionary publicHosts) @@ -329,7 +340,6 @@ Dictionary GetRelevantServerToNodeTypeMap(Dictionary conns) diff --git a/test/Npgsql.Tests/YBFallBackTopologyExtended.cs b/test/Npgsql.Tests/YBFallBackTopologyExtended.cs index 705ba59bb2..fcef9eb425 100644 --- a/test/Npgsql.Tests/YBFallBackTopologyExtended.cs +++ b/test/Npgsql.Tests/YBFallBackTopologyExtended.cs @@ -18,37 +18,69 @@ public class YBFallBackTopologyExtended : YBFallbackTopolgyTests var cmd = "/bin/yb-ctl start --rf 3 --placement_info \"aws.us-west.us-west-1a,aws.us-west.us-west-1a,aws.us-west.us-west-1a\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2a\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2b\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2c\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } void startYBDBClusterWithNineNodes() { string? _Output = null; string? _Error = null; ExecuteShellCommand( "/bin/yb-ctl destroy", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl --rf 3 start --placement_info \"aws.us-west.us-west-1a\" ", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-north.eu-north-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-west.eu-west-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-west.eu-west-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-north.eu-north-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); Thread.Sleep(5000); } @@ -86,18 +118,26 @@ public async Task TestFallback() var cmd = "/bin/yb-ctl stop_node 1"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); count = new[] { -1, -1, -1, -1, 12, 0 }; conns = await CreateConnections(connStringBuilder+"aws.us-west.us-west-1a:1,aws.us-east.us-east-2a:2,aws.us-east.us-east-2b:3,aws.us-east.us-east-2c:4", count); @@ -105,6 +145,8 @@ public async Task TestFallback() cmd = "/bin/yb-ctl start_node 4 --placement_info \"aws.us-east.us-east-2a\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); Thread.Sleep(15000); count = new[] { -1, -1, -1, 12, 0, 0 }; @@ -113,10 +155,14 @@ public async Task TestFallback() cmd = "/bin/yb-ctl start_node 1 --placement_info \"aws.us-west.us-west-1a\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl start_node 2 --placement_info \"aws.us-west.us-west-1a\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); Thread.Sleep(15000); @@ -126,7 +172,7 @@ public async Task TestFallback() DestroyCluster(); } - [Test] + [Test, Timeout(240000)] public async Task CheckMultiNodeDown(){ // Start RF=3 cluster with 9 nodes and with placements (127.0.0.1, 127.0.0.2, 127.0.0.3) -> us-west-1a, // and 127.0.0.4 -> us-east-2a, 127.0.0.5 -> us-east-2a and 127.0.0.6 -> eu-north-2a, 127.0.0.9 -> eu-north-2a, @@ -141,21 +187,48 @@ await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aw ".*:3,aws.eu-north.*:4", new[] { 6, 6, 6, 0, 0, 0, 0, 0, 0 }); ExecuteShellCommand("/bin/yb-ctl stop_node 1", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl stop_node 2", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl stop_node 3", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl stop_node 4", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl stop_node 5", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl stop_node 7", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl stop_node 8", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[] { -1, -1, -1, -1, -1, 9, -1, -1, 9 }); ExecuteShellCommand("/bin/yb-ctl stop_node 9", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[] { -1, -1, -1, -1, -1, 27, -1, -1, -1 }); ExecuteShellCommand("/bin/yb-ctl start_node 2 --placement_info \"aws.us-west.us-west-1a\"", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); Thread.Sleep(15000); @@ -163,10 +236,16 @@ await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aw ".*:3,aws.eu-north.*:4", new[] { -1, 18, -1, -1, -1, 27, -1, -1, -1 }); ExecuteShellCommand("/bin/yb-ctl stop_node 2", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[]{-1, -1, -1, -1, -1, 45, -1, -1, -1 }); ExecuteShellCommand("/bin/yb-ctl start_node 5 --placement_info \"aws.us-east.us-east-2a\"", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); Thread.Sleep(15000); @@ -174,36 +253,57 @@ await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aw ".*:3,aws.eu-north.*:4", new[]{-1, -1, -1, -1, 18, 45, -1, -1, -1}); ExecuteShellCommand("/bin/yb-ctl stop_node 5", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[]{-1, -1, -1, -1, -1, 63, -1, -1, -1}); } finally { ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } } - [Test] + [Test, Timeout(240000)] private async Task checkNodeDownPrimary() { string? _Output = null; string? _Error = null; ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ExecuteShellCommand("/bin/yb-ctl --rf 3 start --placement_info \"aws.us-west.us-west-1a\" ", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { await createConnectionsWithoutCloseAndVerify( "aws.us-west.*:1", new[]{6, 6, 6}); ExecuteShellCommand("/bin/yb-ctl stop_node 1", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1", new[]{-1, 15, 15}); ExecuteShellCommand("/bin/yb-ctl start_node 1 --placement_info \"aws.us-west.us-west-1a\"", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); ClusterAwareDataSource.forceRefresh = true; Thread.Sleep(5000); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1", new[]{16, 16, 16}); } finally { ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } } } diff --git a/test/Npgsql.Tests/YBFallbackTopolgyTests.cs b/test/Npgsql.Tests/YBFallbackTopolgyTests.cs index a0a780ac0c..126314ecac 100644 --- a/test/Npgsql.Tests/YBFallbackTopolgyTests.cs +++ b/test/Npgsql.Tests/YBFallbackTopolgyTests.cs @@ -12,7 +12,7 @@ public class YBFallbackTopolgyTests : YBTestUtils static int mlock = 0; string connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;Timeout=0;Topology Keys="; - [Test] + [Test, Timeout(60000)] public async Task TestFallback1() { CreateCluster(); @@ -20,7 +20,7 @@ public async Task TestFallback1() CloseConnections(conns); DestroyCluster(); } - [Test] + [Test, Timeout(60000)] public async Task TestFallback2() { CreateCluster(); @@ -29,7 +29,7 @@ public async Task TestFallback2() DestroyCluster(); } - [Test] + [Test, Timeout(60000)] public async Task TestFallback3() { CreateCluster(); @@ -38,7 +38,7 @@ public async Task TestFallback3() DestroyCluster(); } - [Test] + [Test, Timeout(60000)] public async Task TestFallback4() { CreateCluster(); @@ -47,7 +47,7 @@ public async Task TestFallback4() DestroyCluster(); } - [Test] + [Test, Timeout(60000)] public async Task TestFallback5() { CreateCluster(); @@ -63,6 +63,8 @@ public async Task TestFallback5() var cmd = "/bin/yb-ctl stop_node 1"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine(_Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var conns = await CreateConnections(connString, new[]{-1, 12, 0}); @@ -70,7 +72,7 @@ public async Task TestFallback5() DestroyCluster(); } - [Test] + [Test, Timeout(60000)] public async Task TestFallback6() { CreateCluster(); @@ -86,6 +88,8 @@ public async Task TestFallback6() var cmd = "/bin/yb-ctl stop_node 1"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine(_Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var conns = await CreateConnections(connString, new[]{-1, 6, 6}); CloseConnections(conns); @@ -155,6 +159,9 @@ protected void CreateCluster() var cmd = "/bin/yb-ctl start --rf 3 --placement_info \"aws.us-west.us-west-2a,aws.us-west.us-west-2b,aws.us-west.us-west-2c\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + System.Threading.Thread.Sleep(5000); } protected void DestroyCluster() @@ -163,6 +170,9 @@ protected void DestroyCluster() string? _Error = null; var cmd = "/bin/yb-ctl destroy"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } } diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index c0d7350a82..d1d728c565 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -12,7 +12,7 @@ public class YBLoadBalancerTests : YBTestUtils { int numConns = 6; - [Test] + [Test, Timeout(60000)] public async Task TestLoadBalance1() { var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=any;Timeout=0"; @@ -43,7 +43,7 @@ public async Task TestLoadBalance1() } } - [Test] + [Test, Timeout(60000)] public async Task TestLoadBalance2() { var connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;YB Servers Refresh Interval=30;Timeout=0"; @@ -60,8 +60,8 @@ public async Task TestLoadBalance2() var cmd = "/bin/yb-ctl stop_node 1"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine(_Output); - - System.Threading.Thread.Sleep(30000); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var conn2 = CreateConnections(connStringBuilder, numConns); conns.AddRange(conn2); @@ -85,7 +85,7 @@ public async Task TestLoadBalance2() } } - [Test] + [Test, Timeout(60000)] public async Task TestLoadBalance3() { var connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;Timeout=0"; @@ -144,6 +144,9 @@ void CreateCluster() var cmd = "/bin/yb-ctl create --rf 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + System.Threading.Thread.Sleep(5000); } void DestroyCluster() @@ -152,6 +155,9 @@ void DestroyCluster() string? _Error = null; var cmd = "/bin/yb-ctl destroy"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } static List CreateConnections(string connString, int numConns) diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs index 8f7a415078..ba7e10e88c 100644 --- a/test/Npgsql.Tests/YBPoolingWrapperTests.cs +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -8,7 +8,7 @@ namespace YBNpgsql.Tests; public class YBPoolingWrapperTests : YBTestUtils { - [Test] + [Test, Timeout(60000)] public void TestPoolingForMultipleConnStrings() { var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; @@ -64,7 +64,7 @@ public void TestPoolingForMultipleConnStrings() } } - [Test] + [Test, Timeout(60000)] public void TestPoolingForMultipleConnStringsWithTopologyKeys() { var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Topology Keys=cloud1.datacenter1.rack1:1;Timeout=0;"; @@ -120,7 +120,7 @@ public void TestPoolingForMultipleConnStringsWithTopologyKeys() } } - [Test] + [Test, Timeout(60000)] public void TestPoolingForMultipleConnStringsMultiThread() { var connStringBuilder1 = "host=127.0.0.1;database=yugabyte;userid=postgres;password=postgres;Load Balance Hosts=any;Timeout=0;"; @@ -233,15 +233,19 @@ void CreateCluster() var cmd = "/bin/yb-ctl create --rf 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - Console.WriteLine("Error:" + _Error); - cmd = "/bin/ysqlsh -c \"CREATE USER tester WITH PASSWORD 'abc123'\""; + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + System.Threading.Thread.Sleep(5000); + cmd = "/bin/ysqlsh -c \\\"CREATE USER tester WITH PASSWORD 'abc123'\\\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - Console.WriteLine("Error:" + _Error); - cmd = "/bin/ysqlsh -c \"GRANT ALL PRIVILEGES ON DATABASE \"yugabyte\" to tester;\""; + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + cmd = "/bin/ysqlsh -c \\\"GRANT ALL PRIVILEGES ON DATABASE \\\"yugabyte\\\" to tester;\\\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - Console.WriteLine("Error:" + _Error); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } void DestroyCluster() @@ -251,6 +255,7 @@ void DestroyCluster() var cmd = "/bin/yb-ctl destroy"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - Console.WriteLine("Error:" + _Error); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } } diff --git a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs index eb3968d405..ffb1332938 100644 --- a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs @@ -42,9 +42,13 @@ public async Task TestOnlyPrimaryAllNodesDownAllPlacement() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -70,9 +74,13 @@ public void TestOnlyPrimaryAllNodesDownInAllPlacementsFallBackToTopologyOnly() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -84,6 +92,10 @@ public void TestOnlyPrimaryAllNodesDownInAllPlacementsFallBackToTopologyOnly() } } + catch (NpgsqlException e) + { + Console.WriteLine("Caught expected exception:" + e.Message); + } finally { CloseConnections(conns); @@ -103,6 +115,8 @@ public async Task TestOnlyPrimaryAllNodesDownPrimarylacement() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -149,6 +163,8 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -173,12 +189,18 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() var cmd = "/bin/yb-ctl stop_node 1"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -190,6 +212,8 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() cmd = "/bin/yb-ctl start_node 1"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); Thread.Sleep(10000); conns.Concat(await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, numConns / 3, numConns / 3, numConns / 3})); @@ -215,9 +239,13 @@ public async Task TestPreferPrimaryAllNodesDownAllPlacement() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -262,6 +290,8 @@ public async Task TestOnlyRRAllNodesDownInPrimaryPlacement() var cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -287,9 +317,13 @@ public async Task TestOnlyRRAllNodesDownInAllPlacement() var cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 5"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -315,9 +349,13 @@ public void TestOnlyRRAllNodesDownInAllPlacementsFallBackToTopologyOnly() var cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 5"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -329,6 +367,10 @@ public void TestOnlyRRAllNodesDownInAllPlacementsFallBackToTopologyOnly() } } + catch (NpgsqlException e) + { + Console.WriteLine("Caught expected Exception:" + e.Message); + } finally { @@ -368,6 +410,8 @@ public async Task TestPreferRRAllNodesDownInPrimaryPlacement() var cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -393,9 +437,13 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() var cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 5"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -421,12 +469,18 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() var cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 5"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 6"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -436,6 +490,9 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() cmd = "/bin/yb-ctl start_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + Thread.Sleep(15000); conns.Concat(await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, numConns, -1, -1})); @@ -478,9 +535,13 @@ public async Task TestAnyAllNodesDownPrimaryPlacement() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -506,15 +567,23 @@ public void TestAnyAllNodesDownAllPlacementFallBackToTopologyOnly() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 5"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -526,7 +595,10 @@ public void TestAnyAllNodesDownAllPlacementFallBackToTopologyOnly() } } - + catch (NpgsqlException e) + { + Console.WriteLine("Caught Expected Exception:" + e.Message); + } finally { CloseConnections(conns); @@ -546,15 +618,23 @@ public async Task TestAnyAllNodesDownAllPlacement() var cmd = "/bin/yb-ctl stop_node 2"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 4"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl stop_node 5"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); try { @@ -610,18 +690,29 @@ void CreateRRCluster() var cmd = "/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); - cmd = "/build/latest/bin/yb-admin --master_addresses 127.0.0.1:7100,127.0.0.2:7100,127.0.0.3:7100 modify_placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 3 live"; + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + cmd = "/bin/yb-admin --master_addresses 127.0.0.1:7100,127.0.0.2:7100,127.0.0.3:7100 modify_placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 3 live"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter2.rack1 --tserver_flags placement_uuid=rr"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter3.rack1 --tserver_flags placement_uuid=rr"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter4.rack1 --tserver_flags placement_uuid=rr"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + System.Threading.Thread.Sleep(5000); } protected void DestroyCluster() @@ -630,6 +721,9 @@ protected void DestroyCluster() string? _Error = null; var cmd = "/bin/yb-ctl destroy"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); } void CloseConnections(List conns) From 88e8dd0390c4b7f9bd2cb07e94a0d72973dd90d6 Mon Sep 17 00:00:00 2001 From: Sfurti-yb Date: Tue, 20 Jan 2026 13:38:36 +0530 Subject: [PATCH 11/13] Upgrade version from 9.0.2.1 to 9.0.2.2 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index fa5d7aead8..2d52b8a8c0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@  - 9.0.2.1 + 9.0.2.2 latest true enable From 7f01b635f73dab5342023663bdfdfe16e5f73608 Mon Sep 17 00:00:00 2001 From: Harsh Daryani Date: Thu, 9 Apr 2026 13:43:29 +0000 Subject: [PATCH 12/13] Fixing Test failures --- src/Npgsql/ClusterAwareDataSource.cs | 46 ++++++++++++++++--- src/Npgsql/TopologyAwareDataSource.cs | 29 +++++++++++- .../YBClusterAwareRRSupportTests.cs | 4 ++ .../YBFallBackTopologyExtended.cs | 19 ++++++-- test/Npgsql.Tests/YBFallbackTopolgyTests.cs | 11 +++++ test/Npgsql.Tests/YBLoadBalancerTests.cs | 4 ++ test/Npgsql.Tests/YBPoolingWrapperTests.cs | 4 ++ test/Npgsql.Tests/YBPreparedStatementsTest.cs | 41 +++++++++++++++-- .../YBTopologyAwareRRSupportTests.cs | 18 ++++++-- 9 files changed, 154 insertions(+), 22 deletions(-) diff --git a/src/Npgsql/ClusterAwareDataSource.cs b/src/Npgsql/ClusterAwareDataSource.cs index d2d3fd86bf..1e13062f20 100644 --- a/src/Npgsql/ClusterAwareDataSource.cs +++ b/src/Npgsql/ClusterAwareDataSource.cs @@ -70,6 +70,11 @@ public class ClusterAwareDataSource: NpgsqlDataSource /// protected static Dictionary poolToNumConnMapRR = new Dictionary(); + /// + /// Preserves connection counts across map rebuilds (e.g. during Refresh). + /// + protected static Dictionary _savedConnectionCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); + /// /// Stores a map of host to their priority /// @@ -286,6 +291,11 @@ public static int GetLoad(string server) } } + if (_savedConnectionCounts.TryGetValue(server, out var savedCount) && savedCount > 0) + { + return savedCount; + } + return 0; } @@ -357,20 +367,43 @@ void UpdateConnectionMap(int poolIndex, int incDec) lock (lockObject) { int currentCount; + var host = _pools[poolIndex].Settings.Host; if (poolToNumConnMapPrimary.ContainsKey(currPool)) { currentCount = poolToNumConnMapPrimary[currPool]; poolToNumConnMapPrimary[currPool] += incDec; _connectionLogger.LogTrace("Updated the current count for {host} from {currentCount} to {newCount}", - _pools[poolIndex].Settings.Host, currentCount, poolToNumConnMapPrimary[currPool]); + host, currentCount, poolToNumConnMapPrimary[currPool]); } else if (poolToNumConnMapRR.ContainsKey(currPool)) { currentCount = poolToNumConnMapRR[currPool]; poolToNumConnMapRR[currPool] += incDec; _connectionLogger.LogTrace("Updated the current count for {host} from {currentCount} to {newCount}", - _pools[poolIndex].Settings.Host, currentCount, poolToNumConnMapRR[currPool]); + host, currentCount, poolToNumConnMapRR[currPool]); + } + else if (incDec > 0) + { + if (_hostsToNodeTypeMap != null && host != null && _hostsToNodeTypeMap.TryGetValue(host, out var nodeType)) + { + if (nodeType.Equals("read_replica", StringComparison.OrdinalIgnoreCase)) + { + poolToNumConnMapRR[currPool] = Math.Max(0, incDec); + } + else + { + poolToNumConnMapPrimary[currPool] = Math.Max(0, incDec); + } + } + } + else if (incDec < 0 && host != null && _savedConnectionCounts.TryGetValue(host, out var savedCount)) + { + var newCount = savedCount + incDec; + if (newCount > 0) + _savedConnectionCounts[host] = newCount; + else + _savedConnectionCounts.Remove(host); } } } @@ -551,9 +584,9 @@ await TryGet(conn, timeoutPerHost, async, preferredType, IsOnline, poolIndex, ex : null); if (connector == null) { - unreachableHostsIndices.Add(poolIndex); + if (!unreachableHostsIndices.Contains(poolIndex)) unreachableHostsIndices.Add(poolIndex); var settingsHost = _pools[poolIndex].Settings.Host; - if (settingsHost != null) unreachableHosts.Add(settingsHost); + if (settingsHost != null && !unreachableHosts.Contains(settingsHost)) unreachableHosts.Add(settingsHost); var pool = _pools[poolIndex]; if (poolToNumConnMapPrimary.ContainsKey(pool)) poolToNumConnMapPrimary.Remove(pool); @@ -637,10 +670,9 @@ await TryGet(conn, timeoutPerHost, async, preferredType, IsOnline, poolIndex, ex { break; } - unreachableHostsIndices.Add(poolIndex); + if (!unreachableHostsIndices.Contains(poolIndex)) unreachableHostsIndices.Add(poolIndex); var settingsHost = _pools[poolIndex].Settings.Host; - if (settingsHost != null) unreachableHosts.Add(settingsHost); - // poolToNumConnMap.Remove(poolIndex); + if (settingsHost != null && !unreachableHosts.Contains(settingsHost)) unreachableHosts.Add(settingsHost); var pool = _pools[poolIndex]; if (poolToNumConnMapPrimary.ContainsKey(pool)) poolToNumConnMapPrimary.Remove(pool); diff --git a/src/Npgsql/TopologyAwareDataSource.cs b/src/Npgsql/TopologyAwareDataSource.cs index 5c88d922ac..48f9ff311f 100644 --- a/src/Npgsql/TopologyAwareDataSource.cs +++ b/src/Npgsql/TopologyAwareDataSource.cs @@ -142,7 +142,28 @@ protected override void CreatePool(Dictionary hostsmap) } if (flag == 1) + { + var existingPool = _pools.First(p => host.Key.Equals(p.Settings.Host, StringComparison.OrdinalIgnoreCase)); + if (host.Value.Equals("primary", StringComparison.OrdinalIgnoreCase)) + { + if (!poolToNumConnMapPrimary.ContainsKey(existingPool)) + { + var restoredCount = _savedConnectionCounts.TryGetValue(host.Key, out var sc) ? sc : 0; + poolToNumConnMapPrimary[existingPool] = restoredCount; + _savedConnectionCounts.Remove(host.Key); + } + } + else if (host.Value.Equals("read_replica", StringComparison.OrdinalIgnoreCase)) + { + if (!poolToNumConnMapRR.ContainsKey(existingPool)) + { + var restoredCount = _savedConnectionCounts.TryGetValue(host.Key, out var sc) ? sc : 0; + poolToNumConnMapRR[existingPool] = restoredCount; + _savedConnectionCounts.Remove(host.Key); + } + } continue; + } var poolSettings = settings.Clone(); poolSettings.Host = host.Key; _connectionLogger.LogDebug("Adding {host} to connection pool", poolSettings.Host); @@ -291,7 +312,6 @@ Dictionary GetRelevantServerToNodeTypeMap(Dictionary GetPrivateOrPublicServers(Dictionary privateHosts, Dictionary publicHosts) @@ -353,7 +373,6 @@ Dictionary GetRelevantServerToNodeTypeMap(Dictionary currentPrivateIPs, string internal override bool Refresh() { _connectionLogger.LogDebug("Refreshing connection"); + foreach (var kvp in poolToNumConnMapPrimary) + if (kvp.Key.Settings.Host != null) + _savedConnectionCounts[kvp.Key.Settings.Host] = kvp.Value; + foreach (var kvp in poolToNumConnMapRR) + if (kvp.Key.Settings.Host != null) + _savedConnectionCounts[kvp.Key.Settings.Host] = kvp.Value; poolToNumConnMapPrimary.Clear(); poolToNumConnMapRR.Clear(); Debug.Assert(initialHosts != null, nameof(initialHosts) + " != null"); diff --git a/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs b/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs index 9c6c37171a..21a3d31c77 100644 --- a/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs @@ -217,6 +217,10 @@ void CreateRRCluster() { string? _Output = null; string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var cmd = "/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); diff --git a/test/Npgsql.Tests/YBFallBackTopologyExtended.cs b/test/Npgsql.Tests/YBFallBackTopologyExtended.cs index fcef9eb425..e7773af043 100644 --- a/test/Npgsql.Tests/YBFallBackTopologyExtended.cs +++ b/test/Npgsql.Tests/YBFallBackTopologyExtended.cs @@ -10,11 +10,15 @@ namespace YBNpgsql.Tests; public class YBFallBackTopologyExtended : YBFallbackTopolgyTests { string connStringBuilder = "host=127.0.0.1,127.0.0.5,127.0.0.7;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;Timeout=0;YB Servers Refresh Interval=10;Topology Keys="; - int numConnections = 12; + int numConnections = 18; new void CreateCluster() { string? _Output = null; string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var cmd = "/bin/yb-ctl start --rf 3 --placement_info \"aws.us-west.us-west-1a,aws.us-west.us-west-1a,aws.us-west.us-west-1a\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); @@ -139,6 +143,8 @@ public async Task TestFallback() if (!string.IsNullOrWhiteSpace(_Error)) Console.WriteLine("Error:" + _Error); + Thread.Sleep(15000); + count = new[] { -1, -1, -1, -1, 12, 0 }; conns = await CreateConnections(connStringBuilder+"aws.us-west.us-west-1a:1,aws.us-east.us-east-2a:2,aws.us-east.us-east-2b:3,aws.us-east.us-east-2c:4", count); @@ -166,7 +172,7 @@ public async Task TestFallback() Thread.Sleep(15000); - count = new[] { 6, 6, -1, -1, -1, -1 }; + count = new[] { 6, 6, -1, 0, 0, 0 }; conns = await CreateConnections(connStringBuilder+"aws.us-west.us-west-1a:1,aws.us-east.us-east-2a:2,aws.us-east.us-east-2b:3,aws.us-east.us-east-2c:4", count); DestroyCluster(); @@ -268,10 +274,13 @@ await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aw } [Test, Timeout(240000)] - private async Task checkNodeDownPrimary() { + public async Task checkNodeDownPrimary() { string? _Output = null; string? _Error = null; + var savedConnString = connStringBuilder; + connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;Timeout=0;YB Servers Refresh Interval=10;Topology Keys="; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); Console.WriteLine("Output:" + _Output); if (!string.IsNullOrWhiteSpace(_Error)) @@ -283,6 +292,7 @@ private async Task checkNodeDownPrimary() { if (!string.IsNullOrWhiteSpace(_Error)) Console.WriteLine("Error:" + _Error); + Thread.Sleep(15000); try { await createConnectionsWithoutCloseAndVerify( "aws.us-west.*:1", new[]{6, 6, 6}); ExecuteShellCommand("/bin/yb-ctl stop_node 1", ref _Output, ref _Error); @@ -300,7 +310,8 @@ private async Task checkNodeDownPrimary() { await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1", new[]{16, 16, 16}); } finally { - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + connStringBuilder = savedConnString; + ExecuteShellCommand("/bin/yb-ctl status", ref _Output, ref _Error); Console.WriteLine("Output:" + _Output); if (!string.IsNullOrWhiteSpace(_Error)) Console.WriteLine("Error:" + _Error); diff --git a/test/Npgsql.Tests/YBFallbackTopolgyTests.cs b/test/Npgsql.Tests/YBFallbackTopolgyTests.cs index 126314ecac..3b07bdd18d 100644 --- a/test/Npgsql.Tests/YBFallbackTopolgyTests.cs +++ b/test/Npgsql.Tests/YBFallbackTopolgyTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -147,6 +148,12 @@ static void CloseConnections(Listconns) conn.Close(); } } + // conn.Close() with pooling enabled returns the connection to the pool + // rather than closing the physical TCP connection. ClearAllPools() + // forces physical disconnection, and the sleep lets the server + // register the disconnections before we verify counts via rpcz. + NpgsqlConnection.ClearAllPools(); + Thread.Sleep(1000); VerifyLocal("127.0.0.1", 0); VerifyLocal("127.0.0.2", 0); VerifyLocal("127.0.0.3", 0); @@ -156,6 +163,10 @@ protected void CreateCluster() { string? _Output = null; string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var cmd = "/bin/yb-ctl start --rf 3 --placement_info \"aws.us-west.us-west-2a,aws.us-west.us-west-2b,aws.us-west.us-west-2c\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index d1d728c565..a8d9f9f9dd 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -141,6 +141,10 @@ void CreateCluster() { string? _Output = null; string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var cmd = "/bin/yb-ctl create --rf 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs index ba7e10e88c..d19dabb657 100644 --- a/test/Npgsql.Tests/YBPoolingWrapperTests.cs +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -230,6 +230,10 @@ void CreateCluster() { string? _Output = null; string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var cmd = "/bin/yb-ctl create --rf 3"; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); diff --git a/test/Npgsql.Tests/YBPreparedStatementsTest.cs b/test/Npgsql.Tests/YBPreparedStatementsTest.cs index 050749d35a..0e5367c736 100644 --- a/test/Npgsql.Tests/YBPreparedStatementsTest.cs +++ b/test/Npgsql.Tests/YBPreparedStatementsTest.cs @@ -1,14 +1,45 @@ using System; +using System.Threading; using System.Threading.Tasks; using YBNpgsqlTypes; using NUnit.Framework; namespace YBNpgsql.Tests; -public class YBPreparedStatementsTest +public class YBPreparedStatementsTest : YBTestUtils { - [Test] + [OneTimeSetUp] + public void SetUp() + { + string? _Output = null; + string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + ExecuteShellCommand("/bin/yb-ctl create --rf 3", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + Thread.Sleep(5000); + + var connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugabyte"; + using var conn = new NpgsqlConnection(connStringBuilder); + conn.Open(); + using var cmd = new NpgsqlCommand("CREATE DATABASE northwind", conn); + try { cmd.ExecuteNonQuery(); } + catch (Exception ex) { Console.WriteLine("northwind DB may already exist: " + ex.Message); } + } + + [OneTimeTearDown] + public void TearDown() + { + string? _Output = null; + string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); + } + [Test] public async Task PreparedStatementsTestWithFlagsEnabled() { var connStringBuilder = "host=localhost;port=5433;database=yugabyte;userid=yugabyte;password=yugabyte;Enable Discard Sequences=false;Enable Discard Temp= false;Enable Close All=false;Load Balance Hosts=true;"; @@ -16,6 +47,8 @@ public async Task PreparedStatementsTestWithFlagsEnabled() try { conn.Open(); + using var dropCmd = new NpgsqlCommand("DROP TABLE IF EXISTS employee", conn); + dropCmd.ExecuteNonQuery(); NpgsqlCommand empCreateCmd = new NpgsqlCommand("CREATE TABLE employee (id int PRIMARY KEY,age int);", conn); empCreateCmd.ExecuteNonQuery(); Console.WriteLine("Created table Employee"); @@ -34,16 +67,14 @@ public async Task PreparedStatementsTestWithFlagsEnabled() await empInsertCommand.ExecuteNonQueryAsync(); } - conn.Close(); } finally { - + conn.Close(); } } [Test] - public void TypeLoadingTimeTest() { var connStringBuilderWithNoTypeLoading = "host=localhost;port=5433;database=northwind;userid=yugabyte;Enable Discard Sequences=false;Enable Discard Temp= false;Load Balance Hosts=true; Server Compatibility Mode=NoTypeLoading"; diff --git a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs index ffb1332938..1753fa35f5 100644 --- a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs @@ -179,10 +179,13 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() [Test, Timeout(60000)] public async Task? TestPreferPrimaryAllPrimaryNodesDown() { - var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugabyte;Load Balance Hosts=preferrr;Topology Keys=cloud1.datacenter2.rack1:1,cloud1.datacenter3.rack1:2;Timeout=0"; + var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugabyte;Load Balance Hosts=preferprimary;Topology Keys=cloud1.datacenter2.rack1:1,cloud1.datacenter3.rack1:2;Timeout=0"; List conns = new List(); CreateRRCluster(); + + conns = await CreateConnections(connStringBuilder, numConns, new []{0, numConns, 0, 0, 0, 0}); + // Stop Node: 127.0.0.1, 127.0.0.2, 127.0.0.3 string? _Output = null; string? _Error = null; @@ -216,7 +219,7 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() Console.WriteLine("Error:" + _Error); Thread.Sleep(10000); - conns.Concat(await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, numConns / 3, numConns / 3, numConns / 3})); + conns.AddRange(await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, numConns / 3, numConns / 3, numConns / 3})); } @@ -459,10 +462,13 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() [Test, Timeout(60000)] public async Task? TestPreferRRAllRRNodesDown() { - var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugabyte;Load Balance Hosts=preferrr;Topology Keys=cloud1.datacenter2.rack1:1,cloud1.datacenter3.rack1:2;Timeout=0"; + var connStringBuilder = "host=127.0.0.1;database=yugabyte;userid=yugabyte;password=yugabyte;Load Balance Hosts=preferrr;Topology Keys=cloud1.datacenter2.rack1:1,cloud1.datacenter3.rack1:2;Timeout=0;YB Servers Refresh Interval=10"; List conns = new List(); CreateRRCluster(); + + conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, numConns, 0, 0}); + // Stop Node: 127.0.0.4, 127.0.0.5, 127.0.0.6 string? _Output = null; string? _Error = null; @@ -494,7 +500,7 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() Console.WriteLine("Error:" + _Error); Thread.Sleep(15000); - conns.Concat(await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, numConns, -1, -1})); + conns.AddRange(await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, numConns, -1, -1})); } @@ -687,6 +693,10 @@ void CreateRRCluster() { string? _Output = null; string? _Error = null; + ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); + Console.WriteLine("Output:" + _Output); + if (!string.IsNullOrWhiteSpace(_Error)) + Console.WriteLine("Error:" + _Error); var cmd = "/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\""; ExecuteShellCommand(cmd, ref _Output, ref _Error ); Console.WriteLine("Output:" + _Output); From 4d42021d865fd0cea816300fa5a3a0a86ec295ff Mon Sep 17 00:00:00 2001 From: Harsh Daryani Date: Mon, 13 Apr 2026 11:07:34 +0000 Subject: [PATCH 13/13] Changes as per review comments --- .../YBClusterAwareRRSupportTests.cs | 87 +----- .../YBFallBackTopologyExtended.cs | 202 +++---------- test/Npgsql.Tests/YBFallbackTopolgyTests.cs | 36 +-- test/Npgsql.Tests/YBLoadBalancerTests.cs | 29 +- test/Npgsql.Tests/YBPoolingWrapperTests.cs | 29 +- test/Npgsql.Tests/YBPreparedStatementsTest.cs | 16 +- test/Npgsql.Tests/YBTestUtils.cs | 81 +++-- .../YBTopologyAwareRRSupportTests.cs | 281 +++--------------- 8 files changed, 146 insertions(+), 615 deletions(-) diff --git a/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs b/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs index 21a3d31c77..60c8230183 100644 --- a/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBClusterAwareRRSupportTests.cs @@ -55,24 +55,9 @@ public async Task TestPreferPrimaryAllNodesDown() List conns = new List(); CreateRRCluster(); // Stop node: 127.0.0.1, 127.0.0.2, 127.0.0.3 - - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); try { @@ -131,24 +116,9 @@ public async Task TestPreferRRAllNodesDown() List conns = new List(); CreateRRCluster(); // Stop node : 127.0.0.4, 127.0.0.5, 127.0.0.6 - - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 6"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); + ExecuteShellCommand("/bin/yb-ctl stop_node 6", "stop node 6"); try { conns = await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, -1, -1, -1}); @@ -215,49 +185,18 @@ static async Task> CreateConnections(string connString, i void CreateRRCluster() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - var cmd = "/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-admin --master_addresses 127.0.0.1:7100,127.0.0.2:7100,127.0.0.3:7100 modify_placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 3 live"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter2.rack1 --tserver_flags placement_uuid=rr"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter3.rack1 --tserver_flags placement_uuid=rr"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter4.rack1 --tserver_flags placement_uuid=rr"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); + ExecuteShellCommand("/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\"", "create cluster"); + ExecuteShellCommand("/bin/yb-admin --master_addresses 127.0.0.1:7100,127.0.0.2:7100,127.0.0.3:7100 modify_placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 3 live", "modify placement info"); + ExecuteShellCommand("/bin/yb-ctl add_node --placement_info cloud1.datacenter2.rack1 --tserver_flags placement_uuid=rr", "add RR node (datacenter2)"); + ExecuteShellCommand("/bin/yb-ctl add_node --placement_info cloud1.datacenter3.rack1 --tserver_flags placement_uuid=rr", "add RR node (datacenter3)"); + ExecuteShellCommand("/bin/yb-ctl add_node --placement_info cloud1.datacenter4.rack1 --tserver_flags placement_uuid=rr", "add RR node (datacenter4)"); System.Threading.Thread.Sleep(5000); } protected void DestroyCluster() { - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl destroy"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); } void CloseConnections(List conns) diff --git a/test/Npgsql.Tests/YBFallBackTopologyExtended.cs b/test/Npgsql.Tests/YBFallBackTopologyExtended.cs index e7773af043..7015fe65ea 100644 --- a/test/Npgsql.Tests/YBFallBackTopologyExtended.cs +++ b/test/Npgsql.Tests/YBFallBackTopologyExtended.cs @@ -13,78 +13,34 @@ public class YBFallBackTopologyExtended : YBFallbackTopolgyTests int numConnections = 18; new void CreateCluster() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); var cmd = "/bin/yb-ctl start --rf 3 --placement_info \"aws.us-west.us-west-1a,aws.us-west.us-west-1a,aws.us-west.us-west-1a\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "start cluster"); cmd = "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2a\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "add node (us-east-2a)"); cmd = "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2b\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "add node (us-east-2b)"); cmd = "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2c\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "add node (us-east-2c)"); } void startYBDBClusterWithNineNodes() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand( "/bin/yb-ctl destroy", ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand( "/bin/yb-ctl destroy", "destroy cluster"); ExecuteShellCommand( "/bin/yb-ctl --rf 3 start --placement_info \"aws.us-west.us-west-1a\" ", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "start cluster"); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2a\"", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "add node (us-east-2a)"); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.us-east.us-east-2a\"", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "add node (us-east-2a)"); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-north.eu-north-2a\"", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - + "add node (eu-north-2a)"); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-west.eu-west-2a\"", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "add node (eu-west-2a)"); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-west.eu-west-2a\"", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "add node (eu-west-2a)"); ExecuteShellCommand( "/bin/yb-ctl add_node --placement_info \"aws.eu-north.eu-north-2a\"", - ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "add node (eu-north-2a)"); Thread.Sleep(5000); } @@ -112,36 +68,21 @@ async Task createConnectionsWithoutCloseAndVerify(string tkValue, int[] counts) public async Task TestFallback() { - string? _Output = null; - string? _Error = null; - CreateCluster(); int[] count = { 4, 4, 4, 0, 0, 0 }; var conns = await CreateConnections(connStringBuilder+"aws.us-west.us-west-1a:1,aws.us-east.us-east-2a:2,aws.us-east.us-east-2b:3,aws.us-east.us-east-2c:4", count); var cmd = "/bin/yb-ctl stop_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "stop node 1"); cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "stop node 2"); cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "stop node 3"); cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "stop node 4"); Thread.Sleep(15000); @@ -149,26 +90,17 @@ public async Task TestFallback() conns = await CreateConnections(connStringBuilder+"aws.us-west.us-west-1a:1,aws.us-east.us-east-2a:2,aws.us-east.us-east-2b:3,aws.us-east.us-east-2c:4", count); cmd = "/bin/yb-ctl start_node 4 --placement_info \"aws.us-east.us-east-2a\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "start node 4 (aws.us-east.us-east-2a)"); Thread.Sleep(15000); count = new[] { -1, -1, -1, 12, 0, 0 }; conns = await CreateConnections(connStringBuilder+"aws.us-west.us-west-1a:1,aws.us-east.us-east-2a:2,aws.us-east.us-east-2b:3,aws.us-east.us-east-2c:4", count); cmd = "/bin/yb-ctl start_node 1 --placement_info \"aws.us-west.us-west-1a\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "start node 1 (aws.us-west.us-west-1a)"); cmd = "/bin/yb-ctl start_node 2 --placement_info \"aws.us-west.us-west-1a\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "start node 2 (aws.us-west.us-west-1a)"); Thread.Sleep(15000); @@ -184,137 +116,79 @@ public async Task CheckMultiNodeDown(){ // and 127.0.0.4 -> us-east-2a, 127.0.0.5 -> us-east-2a and 127.0.0.6 -> eu-north-2a, 127.0.0.9 -> eu-north-2a, // and 127.0.0.7 -> eu-west-2a, 127.0.0.8 -> eu-west-2a. startYBDBClusterWithNineNodes(); - string? _Output = null; - string? _Error = null; try { await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[] { 6, 6, 6, 0, 0, 0, 0, 0, 0 }); - ExecuteShellCommand("/bin/yb-ctl stop_node 1", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - ExecuteShellCommand("/bin/yb-ctl stop_node 2", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - ExecuteShellCommand("/bin/yb-ctl stop_node 3", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - ExecuteShellCommand("/bin/yb-ctl stop_node 4", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - ExecuteShellCommand("/bin/yb-ctl stop_node 5", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - ExecuteShellCommand("/bin/yb-ctl stop_node 7", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - ExecuteShellCommand("/bin/yb-ctl stop_node 8", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); + ExecuteShellCommand("/bin/yb-ctl stop_node 7", "stop node 7"); + ExecuteShellCommand("/bin/yb-ctl stop_node 8", "stop node 8"); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[] { -1, -1, -1, -1, -1, 9, -1, -1, 9 }); - ExecuteShellCommand("/bin/yb-ctl stop_node 9", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 9", "stop node 9"); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[] { -1, -1, -1, -1, -1, 27, -1, -1, -1 }); ExecuteShellCommand("/bin/yb-ctl start_node 2 --placement_info \"aws.us-west.us-west-1a\"", - ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "start node 2 (aws.us-west.us-west-1a)"); Thread.Sleep(15000); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[] { -1, 18, -1, -1, -1, 27, -1, -1, -1 }); - ExecuteShellCommand("/bin/yb-ctl stop_node 2", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[]{-1, -1, -1, -1, -1, 45, -1, -1, -1 }); - ExecuteShellCommand("/bin/yb-ctl start_node 5 --placement_info \"aws.us-east.us-east-2a\"", ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl start_node 5 --placement_info \"aws.us-east.us-east-2a\"", "start node 5 (aws.us-east.us-east-2a)"); Thread.Sleep(15000); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[]{-1, -1, -1, -1, 18, 45, -1, -1, -1}); - ExecuteShellCommand("/bin/yb-ctl stop_node 5", ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1,aws.us-east.*:2,aws.eu-west" + ".*:3,aws.eu-north.*:4", new[]{-1, -1, -1, -1, -1, 63, -1, -1, -1}); } finally { - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); } } [Test, Timeout(240000)] public async Task checkNodeDownPrimary() { - string? _Output = null; - string? _Error = null; var savedConnString = connStringBuilder; connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugsbyte;Load Balance Hosts=true;Timeout=0;YB Servers Refresh Interval=10;Topology Keys="; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); ExecuteShellCommand("/bin/yb-ctl --rf 3 start --placement_info \"aws.us-west.us-west-1a\" ", - ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "start cluster"); Thread.Sleep(15000); try { await createConnectionsWithoutCloseAndVerify( "aws.us-west.*:1", new[]{6, 6, 6}); - ExecuteShellCommand("/bin/yb-ctl stop_node 1", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1", new[]{-1, 15, 15}); ExecuteShellCommand("/bin/yb-ctl start_node 1 --placement_info \"aws.us-west.us-west-1a\"", - ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + "start node 1 (aws.us-west.us-west-1a)"); ClusterAwareDataSource.forceRefresh = true; Thread.Sleep(5000); await createConnectionsWithoutCloseAndVerify("aws.us-west.*:1", new[]{16, 16, 16}); } finally { connStringBuilder = savedConnString; - ExecuteShellCommand("/bin/yb-ctl status", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl status", "cluster status"); } } } diff --git a/test/Npgsql.Tests/YBFallbackTopolgyTests.cs b/test/Npgsql.Tests/YBFallbackTopolgyTests.cs index 3b07bdd18d..e69f0e011e 100644 --- a/test/Npgsql.Tests/YBFallbackTopolgyTests.cs +++ b/test/Npgsql.Tests/YBFallbackTopolgyTests.cs @@ -59,13 +59,7 @@ public async Task TestFallback5() await VerifyOn("127.0.0.1", 1); - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); var conns = await CreateConnections(connString, new[]{-1, 12, 0}); @@ -84,13 +78,7 @@ public async Task TestFallback6() await VerifyOn("127.0.0.1", 1); - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); var conns = await CreateConnections(connString, new[]{-1, 6, 6}); CloseConnections(conns); @@ -161,29 +149,15 @@ static void CloseConnections(Listconns) protected void CreateCluster() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); var cmd = "/bin/yb-ctl start --rf 3 --placement_info \"aws.us-west.us-west-2a,aws.us-west.us-west-2b,aws.us-west.us-west-2c\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "start cluster"); System.Threading.Thread.Sleep(5000); } protected void DestroyCluster() { - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl destroy"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); } } diff --git a/test/Npgsql.Tests/YBLoadBalancerTests.cs b/test/Npgsql.Tests/YBLoadBalancerTests.cs index a8d9f9f9dd..da26b00fb9 100644 --- a/test/Npgsql.Tests/YBLoadBalancerTests.cs +++ b/test/Npgsql.Tests/YBLoadBalancerTests.cs @@ -55,13 +55,7 @@ public async Task TestLoadBalance2() var conn1 = CreateConnections(connStringBuilder, numConns); conns.AddRange(conn1); - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine(_Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); var conn2 = CreateConnections(connStringBuilder, numConns); conns.AddRange(conn2); @@ -139,29 +133,14 @@ public async Task TestLoadBalance3() void CreateCluster() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - var cmd = "/bin/yb-ctl create --rf 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); + ExecuteShellCommand("/bin/yb-ctl create --rf 3", "create cluster"); System.Threading.Thread.Sleep(5000); } void DestroyCluster() { - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl destroy"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); } static List CreateConnections(string connString, int numConns) diff --git a/test/Npgsql.Tests/YBPoolingWrapperTests.cs b/test/Npgsql.Tests/YBPoolingWrapperTests.cs index d19dabb657..9764d75fd3 100644 --- a/test/Npgsql.Tests/YBPoolingWrapperTests.cs +++ b/test/Npgsql.Tests/YBPoolingWrapperTests.cs @@ -228,38 +228,19 @@ static List CreateConnections(string connString, int numConns) } void CreateCluster() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); var cmd = "/bin/yb-ctl create --rf 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "create cluster"); System.Threading.Thread.Sleep(5000); cmd = "/bin/ysqlsh -c \\\"CREATE USER tester WITH PASSWORD 'abc123'\\\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "create user tester"); cmd = "/bin/ysqlsh -c \\\"GRANT ALL PRIVILEGES ON DATABASE \\\"yugabyte\\\" to tester;\\\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "grant privileges to tester"); } void DestroyCluster() { - string? _Output = null; - string? _Error = null; var cmd = "/bin/yb-ctl destroy"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand(cmd, "destroy cluster"); } } diff --git a/test/Npgsql.Tests/YBPreparedStatementsTest.cs b/test/Npgsql.Tests/YBPreparedStatementsTest.cs index 0e5367c736..bb1b313499 100644 --- a/test/Npgsql.Tests/YBPreparedStatementsTest.cs +++ b/test/Npgsql.Tests/YBPreparedStatementsTest.cs @@ -11,13 +11,8 @@ public class YBPreparedStatementsTest : YBTestUtils [OneTimeSetUp] public void SetUp() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - ExecuteShellCommand("/bin/yb-ctl create --rf 3", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); + ExecuteShellCommand("/bin/yb-ctl create --rf 3", "create cluster"); Thread.Sleep(5000); var connStringBuilder = "host=127.0.0.1;port=5433;database=yugabyte;userid=yugabyte;password=yugabyte"; @@ -31,12 +26,7 @@ public void SetUp() [OneTimeTearDown] public void TearDown() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); } [Test] diff --git a/test/Npgsql.Tests/YBTestUtils.cs b/test/Npgsql.Tests/YBTestUtils.cs index c339fb7d44..df04aa094d 100644 --- a/test/Npgsql.Tests/YBTestUtils.cs +++ b/test/Npgsql.Tests/YBTestUtils.cs @@ -10,55 +10,48 @@ namespace YBNpgsql.Tests; public class YBTestUtils { - public void ExecuteShellCommand(string argument, ref string? _outputMessage, ref string? _errorMessage) -{ - var path = Environment.GetEnvironmentVariable("YBDB_PATH"); - if (path == null) - { - throw new ArgumentException("YBDB_PATH not initialized"); - } - var arguments = path + argument; - // Set process variable - // Provides access to local and remote processes and enables you to start and stop local system processes. - Process? _Process = null; - try + static readonly string YbdbPath = Environment.GetEnvironmentVariable("YBDB_PATH") + ?? throw new ArgumentException("YBDB_PATH not initialized"); + + public void ExecuteShellCommand(string argument, string message) { - ProcessStartInfo startInfo = new ProcessStartInfo() - { - FileName = "/bin/bash", - Arguments = " -c \"" + arguments + " \"", - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardInput = true, - RedirectStandardError = true, - }; - _Process = new Process() + var arguments = YbdbPath + argument; + Process? process = null; + try { - StartInfo = startInfo, - }; - _Process.Start(); + var startInfo = new ProcessStartInfo() + { + FileName = "/bin/bash", + Arguments = " -c \"" + arguments + " \"", + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardInput = true, + RedirectStandardError = true, + }; + process = new Process() { StartInfo = startInfo }; + Console.WriteLine("Executing command to " + message); + process.Start(); - // Instructs the Process component to wait indefinitely for the associated process to exit. - _errorMessage = _Process.StandardError.ReadToEnd(); - _Process.WaitForExit(); + var error = process.StandardError.ReadToEnd(); + process.WaitForExit(); + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); - // Instructs the Process component to wait indefinitely for the associated process to exit. - _outputMessage = _Process.StandardOutput.ReadToEnd(); - _Process.WaitForExit(); - } - catch (Exception _Exception) - { - // Error - Console.WriteLine("Exception caught in process: {0}", _Exception.ToString()); - } - finally - { - // close process and do cleanup - _Process?.Close(); - _Process?.Dispose(); - _Process = null!; + Console.WriteLine("Output:" + output); + if (!string.IsNullOrWhiteSpace(error)) + Console.WriteLine("Error:" + error); + } + catch (Exception ex) + { + Console.WriteLine("Exception caught in process: {0}", ex); + throw; + } + finally + { + process?.Close(); + process?.Dispose(); + } } -} protected static async Task VerifyOn(string server, int ExpectedCount) { diff --git a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs index 1753fa35f5..33e87ca2c5 100644 --- a/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs +++ b/test/Npgsql.Tests/YBTopologyAwareRRSupportTests.cs @@ -37,18 +37,8 @@ public async Task TestOnlyPrimaryAllNodesDownAllPlacement() List conns = new List(); CreateRRCluster(); // Stop node : 127.0.0.2, 127.0.0.3 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); try { @@ -69,18 +59,8 @@ public void TestOnlyPrimaryAllNodesDownInAllPlacementsFallBackToTopologyOnly() List conns = new List(); CreateRRCluster(); // Stop node : 127.0.0.2, 127.0.0.3 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); try { @@ -110,13 +90,7 @@ public async Task TestOnlyPrimaryAllNodesDownPrimarylacement() List conns = new List(); CreateRRCluster(); // Stop node : 127.0.0.2 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); try { @@ -158,13 +132,7 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() List conns = new List(); CreateRRCluster(); // Stop node : 127.0.0.2 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); try { @@ -187,36 +155,16 @@ public async Task TestPreferPrimaryAllNodesDownPrimaryPlacement() conns = await CreateConnections(connStringBuilder, numConns, new []{0, numConns, 0, 0, 0, 0}); // Stop Node: 127.0.0.1, 127.0.0.2, 127.0.0.3 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 1", "stop node 1"); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); try { conns = await CreateConnections(connStringBuilder, numConns, new []{-1, -1, -1, numConns / 3, numConns / 3, numConns / 3}); // Start Node 1 - _Output = null; - _Error = null; - cmd = "/bin/yb-ctl start_node 1"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl start_node 1", "start node 1"); Thread.Sleep(10000); conns.AddRange(await CreateConnections(connStringBuilder, numConns, new []{numConns, -1, -1, numConns / 3, numConns / 3, numConns / 3})); @@ -237,18 +185,8 @@ public async Task TestPreferPrimaryAllNodesDownAllPlacement() List conns = new List(); CreateRRCluster(); // Stop node : 127.0.0.2, 127.0.0.3 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); try { @@ -288,13 +226,7 @@ public async Task TestOnlyRRAllNodesDownInPrimaryPlacement() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.4 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); try { @@ -315,18 +247,8 @@ public async Task TestOnlyRRAllNodesDownInAllPlacement() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.4, 127.0.0.5 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); try { @@ -347,18 +269,8 @@ public void TestOnlyRRAllNodesDownInAllPlacementsFallBackToTopologyOnly() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.4, 127.0.0.5 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); try { @@ -408,13 +320,7 @@ public async Task TestPreferRRAllNodesDownInPrimaryPlacement() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.4 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); try { @@ -435,18 +341,8 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.4, 127.0.0.5 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); try { @@ -470,34 +366,16 @@ public async Task TestPreferRRAllNodesDownInAllPlacements() conns = await CreateConnections(connStringBuilder, numConns, new []{0, 0, 0, numConns, 0, 0}); // Stop Node: 127.0.0.4, 127.0.0.5, 127.0.0.6 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 6"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); + ExecuteShellCommand("/bin/yb-ctl stop_node 6", "stop node 6"); try { conns = await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, -1, -1, -1}); // Start RR node: 127.0.0.4 - cmd = "/bin/yb-ctl start_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl start_node 4", "start node 4"); Thread.Sleep(15000); conns.AddRange(await CreateConnections(connStringBuilder, numConns, new []{numConns / 3, numConns / 3, numConns / 3, numConns, -1, -1})); @@ -536,18 +414,8 @@ public async Task TestAnyAllNodesDownPrimaryPlacement() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.2, 127.0.0.4 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); try { @@ -568,28 +436,10 @@ public void TestAnyAllNodesDownAllPlacementFallBackToTopologyOnly() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.2, 127.0.0.3, 127.0.0.4, 127.0.0.5 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); try { @@ -619,28 +469,10 @@ public async Task TestAnyAllNodesDownAllPlacement() List conns = new List(); CreateRRCluster(); // Stop Node: 127.0.0.2, 127.0.0.3, 127.0.0.4, 127.0.0.5 - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl stop_node 2"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 3"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 4"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl stop_node 5"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl stop_node 2", "stop node 2"); + ExecuteShellCommand("/bin/yb-ctl stop_node 3", "stop node 3"); + ExecuteShellCommand("/bin/yb-ctl stop_node 4", "stop node 4"); + ExecuteShellCommand("/bin/yb-ctl stop_node 5", "stop node 5"); try { @@ -691,49 +523,18 @@ static async Task> CreateConnections(string connString, i void CreateRRCluster() { - string? _Output = null; - string? _Error = null; - ExecuteShellCommand("/bin/yb-ctl destroy", ref _Output, ref _Error); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - var cmd = "/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\""; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-admin --master_addresses 127.0.0.1:7100,127.0.0.2:7100,127.0.0.3:7100 modify_placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 3 live"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter2.rack1 --tserver_flags placement_uuid=rr"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter3.rack1 --tserver_flags placement_uuid=rr"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); - cmd = "/bin/yb-ctl add_node --placement_info cloud1.datacenter4.rack1 --tserver_flags placement_uuid=rr"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); + ExecuteShellCommand("/bin/yb-ctl create --rf 3 --placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 --tserver_flags \"placement_uuid=live,max_stale_read_bound_time_ms=60000000\"", "create cluster"); + ExecuteShellCommand("/bin/yb-admin --master_addresses 127.0.0.1:7100,127.0.0.2:7100,127.0.0.3:7100 modify_placement_info cloud1.datacenter1.rack1,cloud1.datacenter2.rack1,cloud1.datacenter3.rack1 3 live", "modify placement info"); + ExecuteShellCommand("/bin/yb-ctl add_node --placement_info cloud1.datacenter2.rack1 --tserver_flags placement_uuid=rr", "add RR node (datacenter2)"); + ExecuteShellCommand("/bin/yb-ctl add_node --placement_info cloud1.datacenter3.rack1 --tserver_flags placement_uuid=rr", "add RR node (datacenter3)"); + ExecuteShellCommand("/bin/yb-ctl add_node --placement_info cloud1.datacenter4.rack1 --tserver_flags placement_uuid=rr", "add RR node (datacenter4)"); System.Threading.Thread.Sleep(5000); } protected void DestroyCluster() { - string? _Output = null; - string? _Error = null; - var cmd = "/bin/yb-ctl destroy"; - ExecuteShellCommand(cmd, ref _Output, ref _Error ); - Console.WriteLine("Output:" + _Output); - if (!string.IsNullOrWhiteSpace(_Error)) - Console.WriteLine("Error:" + _Error); + ExecuteShellCommand("/bin/yb-ctl destroy", "destroy cluster"); } void CloseConnections(List conns)