This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 31292e3213d IGNITE-26312 .NET: Fix port issues in compat tests (#6545)
31292e3213d is described below

commit 31292e3213d0621bc114ee1318aa82fbc0033090
Author: Pavel Tupitsyn <ptupit...@apache.org>
AuthorDate: Wed Sep 3 17:02:13 2025 +0300

    IGNITE-26312 .NET: Fix port issues in compat tests (#6545)
---
 .../PlatformCompatibilityTestNodeRunner.java       |  33 ++++---
 .../CurrentClientWithOldServerCompatibilityTest.cs |   7 +-
 .../dotnet/Apache.Ignite.Tests/JavaServer.cs       | 106 +++++++++++++--------
 3 files changed, 89 insertions(+), 57 deletions(-)

diff --git 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/PlatformCompatibilityTestNodeRunner.java
 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/PlatformCompatibilityTestNodeRunner.java
index 87d08986c14..9b19fe29992 100644
--- 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/PlatformCompatibilityTestNodeRunner.java
+++ 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/PlatformCompatibilityTestNodeRunner.java
@@ -17,10 +17,6 @@
 
 package org.apache.ignite.internal;
 
-import static 
org.apache.ignite.internal.ClusterConfiguration.DEFAULT_BASE_CLIENT_PORT;
-import static 
org.apache.ignite.internal.ClusterConfiguration.DEFAULT_BASE_HTTPS_PORT;
-import static 
org.apache.ignite.internal.ClusterConfiguration.DEFAULT_BASE_HTTP_PORT;
-import static 
org.apache.ignite.internal.ClusterConfiguration.DEFAULT_BASE_PORT;
 import static 
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIMEM_PROFILE_NAME;
 import static 
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIPERSIST_PROFILE_NAME;
 import static 
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_ROCKSDB_PROFILE_NAME;
@@ -59,22 +55,21 @@ public class PlatformCompatibilityTestNodeRunner {
      * @param args Args.
      */
     public static void main(String[] args) throws Exception {
-        String version = System.getenv("IGNITE_OLD_SERVER_VERSION");
-        String workDir = System.getenv("IGNITE_OLD_SERVER_WORK_DIR");
-        int portOffset = 
Integer.parseInt(System.getenv().getOrDefault("IGNITE_OLD_SERVER_PORT_OFFSET", 
"20000"));
+        String version = getEnvOrThrow("IGNITE_OLD_SERVER_VERSION");
+        String workDir = getEnvOrThrow("IGNITE_OLD_SERVER_WORK_DIR");
 
-        if (version == null || workDir == null) {
-            throw new Exception("IGNITE_OLD_SERVER_VERSION and 
IGNITE_OLD_SERVER_WORK_DIR environment variables are not set.");
-        }
+        int port = Integer.parseInt(getEnvOrThrow("IGNITE_OLD_SERVER_PORT"));
+        int httpPort = 
Integer.parseInt(getEnvOrThrow("IGNITE_OLD_SERVER_HTTP_PORT"));
+        int clientPort = 
Integer.parseInt(getEnvOrThrow("IGNITE_OLD_SERVER_CLIENT_PORT"));
 
         System.out.println(">>> Starting test node with version: " + version + 
" in work directory: " + workDir);
+        System.out.println(">>> Ports: node=" + port + ", http=" + httpPort + 
", client=" + clientPort);
 
         ClusterConfiguration clusterConfiguration = 
ClusterConfiguration.builder(new PlatformTestInfo(), Path.of(workDir))
                 
.defaultNodeBootstrapConfigTemplate(NODE_BOOTSTRAP_CFG_TEMPLATE)
-                .basePort(DEFAULT_BASE_PORT + portOffset)
-                .baseHttpPort(DEFAULT_BASE_HTTP_PORT + portOffset)
-                .baseHttpsPort(DEFAULT_BASE_HTTPS_PORT + portOffset)
-                .baseClientPort(DEFAULT_BASE_CLIENT_PORT + portOffset)
+                .basePort(port)
+                .baseHttpPort(httpPort)
+                .baseClientPort(clientPort)
                 .build();
 
         var cluster = new IgniteCluster(clusterConfiguration);
@@ -92,6 +87,16 @@ public class PlatformCompatibilityTestNodeRunner {
         cluster.stop();
     }
 
+    private static String getEnvOrThrow(String name) throws Exception {
+        String val = System.getenv(name);
+
+        if (val == null || val.isEmpty()) {
+            throw new Exception(name + " environment variable is not set or 
empty.");
+        }
+
+        return val;
+    }
+
     private static class PlatformTestInfo implements TestInfo {
         @Override
         public String getDisplayName() {
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
index 1c3646e2975..a9fc4e62e34 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compatibility/CurrentClientWithOldServerCompatibilityTest.cs
@@ -75,8 +75,11 @@ public class CurrentClientWithOldServerCompatibilityTest
     [OneTimeTearDown]
     public void OneTimeTearDown()
     {
-        _client.Dispose();
-        _javaServer.Dispose();
+        // ReSharper disable 
ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
+        _client?.Dispose();
+        _javaServer?.Dispose();
+
+        // ReSharper restore 
ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
         _workDir.Dispose();
     }
 
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/JavaServer.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/JavaServer.cs
index 74392f3dc5a..12d02b80233 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/JavaServer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/JavaServer.cs
@@ -23,6 +23,8 @@ namespace Apache.Ignite.Tests
     using System.Globalization;
     using System.IO;
     using System.Linq;
+    using System.Net;
+    using System.Net.Sockets;
     using System.Runtime.InteropServices;
     using System.Threading;
     using System.Threading.Tasks;
@@ -36,10 +38,6 @@ namespace Apache.Ignite.Tests
         private const string GradleOptsEnvVar = "IGNITE_DOTNET_GRADLE_OPTS";
         private const string RequireExternalJavaServerEnvVar = 
"IGNITE_DOTNET_REQUIRE_EXTERNAL_SERVER";
 
-        private const int DefaultClientPort = 10942;
-
-        private const int DefaultClientPortOldServer = 10800;
-
         private const int ConnectTimeoutSeconds = 4 * 60;
 
         private const string GradleCommandExec = 
":ignite-runner:runnerPlatformTest --parallel";
@@ -66,13 +64,12 @@ namespace Apache.Ignite.Tests
         /// Starts a server node.
         /// </summary>
         /// <returns>Disposable object to stop the server.</returns>
-        public static async Task<JavaServer> StartAsync() => await 
StartInternalAsync(old: false, env: []);
+        public static async Task<JavaServer> StartAsync() => await 
StartInternalAsync(old: false, env: [], defaultPort: 10942);
 
         public static async Task<JavaServer> StartOldAsync(string version, 
string workDir)
         {
-            // Calculate port offset based on the server version (minor + 
patch) to avoid conflicts with other tests.
-            // This way we can have multiple active nodes with different 
versions (separate clusters).
-            var portOffset = 20_000 + int.Parse(version[2..].Replace(".", 
string.Empty));
+            // Get random unused ports to avoid conflicts with other tests.
+            var ports = GetUnusedPorts(3);
 
             return await StartInternalAsync(
                 old: true,
@@ -80,9 +77,10 @@ namespace Apache.Ignite.Tests
                 {
                     { "IGNITE_OLD_SERVER_VERSION", version },
                     { "IGNITE_OLD_SERVER_WORK_DIR", workDir },
-                    { "IGNITE_OLD_SERVER_PORT_OFFSET", 
portOffset.ToString(CultureInfo.InvariantCulture) }
-                },
-                portOffset: portOffset);
+                    { "IGNITE_OLD_SERVER_PORT", 
ports[0].ToString(CultureInfo.InvariantCulture) },
+                    { "IGNITE_OLD_SERVER_HTTP_PORT", 
ports[1].ToString(CultureInfo.InvariantCulture) },
+                    { "IGNITE_OLD_SERVER_CLIENT_PORT", 
ports[2].ToString(CultureInfo.InvariantCulture) },
+                });
         }
 
         public void Dispose()
@@ -109,17 +107,16 @@ namespace Apache.Ignite.Tests
             Log(">>> Java server stopped.");
         }
 
-        private static async Task<JavaServer> StartInternalAsync(bool old, 
Dictionary<string, string?> env, int portOffset = 0)
+        private static async Task<JavaServer> StartInternalAsync(bool old, 
Dictionary<string, string?> env, int? defaultPort = null)
         {
             string gradleCommand = old ? GradleCommandExecOldServer : 
GradleCommandExec;
-            int defaultPort = (old ? DefaultClientPortOldServer : 
DefaultClientPort) + portOffset;
 
-            if (await TryConnect(defaultPort) == null)
+            if (defaultPort != null && await TryConnect(defaultPort.Value) == 
null)
             {
                 // Server started from outside.
                 Log(">>> Java server is already started on port " + 
defaultPort + ".");
 
-                return new JavaServer([defaultPort], null);
+                return new JavaServer([defaultPort.Value], null);
             }
 
             if 
(bool.TryParse(Environment.GetEnvironmentVariable(RequireExternalJavaServerEnvVar),
 out var requireExternalServer)
@@ -133,8 +130,7 @@ namespace Apache.Ignite.Tests
 
             var process = CreateProcess(gradleCommand, env);
 
-            var evt = new ManualResetEventSlim(false);
-            int[]? ports = null;
+            var tcs = new TaskCompletionSource<int[]>();
 
             DataReceivedEventHandler handler = (_, eventArgs) =>
             {
@@ -148,8 +144,14 @@ namespace Apache.Ignite.Tests
 
                 if (line.StartsWith("THIN_CLIENT_PORTS", 
StringComparison.Ordinal))
                 {
-                    ports = 
line.Split('=').Last().Split(',').Select(int.Parse).OrderBy(x => x).ToArray();
-                    evt.Set();
+                    var ports = 
line.Split('=').Last().Split(',').Select(int.Parse).OrderBy(x => x).ToArray();
+                    tcs.SetResult(ports);
+                }
+
+                if (line.StartsWith("Exception in thread \"main\"", 
StringComparison.OrdinalIgnoreCase))
+                {
+                    process.Kill(entireProcessTree: true);
+                    tcs.SetException(new Exception($"Java server failed: 
{line}"));
                 }
             };
 
@@ -158,37 +160,32 @@ namespace Apache.Ignite.Tests
 
             process.Start();
 
-            process.BeginOutputReadLine();
-            process.BeginErrorReadLine();
-
-            if (!evt.Wait(TimeSpan.FromSeconds(ConnectTimeoutSeconds)))
+            try
             {
-                process.Kill(entireProcessTree: true);
+                process.BeginOutputReadLine();
+                process.BeginErrorReadLine();
 
-                throw new InvalidOperationException("Failed to wait for 
THIN_CLIENT_PORTS. Check logs for details.");
-            }
+                var ports = await 
tcs.Task.WaitAsync(TimeSpan.FromSeconds(ConnectTimeoutSeconds));
+                var port = ports.FirstOrDefault();
 
-            if (ports == null)
-            {
-                process.Kill(entireProcessTree: true);
+                if (!WaitForServer(port))
+                {
+                    process.Kill(entireProcessTree: true);
+                    KillProcessesOnPorts(ports);
 
-                throw new InvalidOperationException("Failed to get ports. 
Check logs for details.");
-            }
+                    throw new InvalidOperationException(
+                        $"Failed to wait for the server to start (can't 
connect the client on port {port}). Check logs for details.");
+                }
 
-            var port = ports.FirstOrDefault();
+                Log($">>> Java server started on port {port}.");
 
-            if (!WaitForServer(port))
+                return new JavaServer(ports, process);
+            }
+            catch (Exception)
             {
                 process.Kill(entireProcessTree: true);
-                KillProcessesOnPorts(ports);
-
-                throw new InvalidOperationException(
-                    $"Failed to wait for the server to start (can't connect 
the client on port {port}). Check logs for details.");
+                throw;
             }
-
-            Log($">>> Java server started on port {port}.");
-
-            return new JavaServer(ports, process);
         }
 
         private static Process CreateProcess(string gradleCommand, 
IDictionary<string, string?> env)
@@ -331,5 +328,32 @@ namespace Apache.Ignite.Tests
             using var process = Process.Start(psi);
             process?.WaitForExit();
         }
+
+        private static int[] GetUnusedPorts(int count)
+        {
+            var ports = new int[count];
+            var listeners = new List<TcpListener>();
+
+            try
+            {
+                for (var i = 0; i < count; i++)
+                {
+                    var listener = new TcpListener(IPAddress.Loopback, 0);
+                    listeners.Add(listener);
+
+                    listener.Start();
+                    ports[i] = ((IPEndPoint)listener.LocalEndpoint).Port;
+                }
+
+                return ports;
+            }
+            finally
+            {
+                foreach (var listener in listeners)
+                {
+                    listener.Stop();
+                }
+            }
+        }
     }
 }

Reply via email to