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 36f75c7168 IGNITE-19151 Add client connections benchmark (#1860)
36f75c7168 is described below

commit 36f75c7168038de2d478f01d4bab1e091267126a
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Wed Mar 29 11:48:46 2023 +0300

    IGNITE-19151 Add client connections benchmark (#1860)
---
 .../ManyConnectionsBenchmark.cs                    | 100 +++++++++++++++++++++
 .../dotnet/Apache.Ignite.Benchmarks/Program.cs     |   7 +-
 modules/runner/build.gradle                        |   9 ++
 .../runner/app/PlatformBenchmarkNodeRunner.java    |  73 +++++++++++++++
 .../runner/app/PlatformTestNodeRunner.java         |  60 ++++++++-----
 5 files changed, 223 insertions(+), 26 deletions(-)

diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ManyConnectionsBenchmark.cs 
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ManyConnectionsBenchmark.cs
new file mode 100644
index 0000000000..117d034426
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ManyConnectionsBenchmark.cs
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Benchmarks;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using Log;
+
+/// <summary>
+/// Establishes many connections to the server node to see how it affects 
server-side performance.
+/// <para />
+/// Requires a running Ignite node started with PlatformBenchmarkNodeRunner:
+/// <code>gradlew :ignite-runner:runnerPlatformBenchmark</code>.
+/// <para />
+/// Results on i9-12900H, .NET SDK 6.0.405, Ubuntu 22.04:
+///
+/// HeartbeatInterval 5 minutes:
+/// 250000 connections established in 00:00:41.0241231.
+/// 250000 GetTable calls in 00:00:16.2082830.
+///
+/// | Connections | JVM Used Heap (MB) |
+/// | ----------- | ------------------ |
+/// | 0           | 155                |
+/// | 10000       | 185                |
+/// | 20000       | 212                |
+/// | 40000       | 265                |
+/// | 80000       | 372                |
+/// | 160000      | 586                |.
+///
+/// HeartbeatInterval 1 second:
+/// 250000 connections established in 00:01:09.8284032.
+/// 250000 GetTable calls in 00:00:51.2001008.
+/// </summary>
+public static class ManyConnectionsBenchmark
+{
+    private const int Connections = 250_000;
+
+    public static async Task RunAsync()
+    {
+        var cfg = new IgniteClientConfiguration
+        {
+            RetryPolicy = new RetryNonePolicy(),
+            Logger = new ConsoleLogger { MinLevel = LogLevel.Warn },
+            HeartbeatInterval = TimeSpan.FromMinutes(5)
+        };
+
+        var clients = new List<IIgniteClient>();
+
+        Console.WriteLine("Establishing connections...");
+        var sw = Stopwatch.StartNew();
+        TimeSpan lastElapsed = default;
+
+        for (int i = 0; i < Connections; i++)
+        {
+            // Use different loopback addresses to avoid running out of ports.
+            var addr = $"127.0.0.{i % 255}:10420";
+
+            cfg.Endpoints.Clear();
+            cfg.Endpoints.Add(addr);
+
+            clients.Add(await IgniteClient.StartAsync(cfg));
+
+            if (sw.Elapsed - lastElapsed > TimeSpan.FromSeconds(2))
+            {
+                lastElapsed = sw.Elapsed;
+                Console.WriteLine($"{i} connections established in 
{sw.Elapsed}.");
+            }
+        }
+
+        Console.WriteLine($"{Connections} connections established in 
{sw.Elapsed}.");
+        Console.WriteLine("Invoking GetTable...");
+        sw = Stopwatch.StartNew();
+
+        foreach (var client in clients)
+        {
+            await client.Tables.GetTableAsync("t");
+        }
+
+        Console.WriteLine($"{Connections} GetTable calls in {sw.Elapsed}.");
+        Console.WriteLine("Press any key to close connections...");
+        Console.ReadKey();
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs 
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs
index 99e1290a76..50b2e2bfb0 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs
@@ -17,13 +17,12 @@
 
 namespace Apache.Ignite.Benchmarks;
 
-using BenchmarkDotNet.Running;
-using Table;
+using System.Threading.Tasks;
 
 internal static class Program
 {
-    private static void Main()
+    private static async Task Main()
     {
-        BenchmarkRunner.Run<TupleGetBenchmarks>();
+        await ManyConnectionsBenchmark.RunAsync();
     }
 }
diff --git a/modules/runner/build.gradle b/modules/runner/build.gradle
index a5552a8972..33ff65ebbd 100644
--- a/modules/runner/build.gradle
+++ b/modules/runner/build.gradle
@@ -116,6 +116,7 @@ dependencies {
         exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
     }
     integrationTestImplementation project(':ignite-metastorage')
+    integrationTestImplementation project(':ignite-metrics')
     integrationTestImplementation project(':ignite-table')
     integrationTestImplementation project(':ignite-transactions')
     integrationTestImplementation project(':ignite-code-deployment')
@@ -155,6 +156,14 @@ tasks.register("runnerPlatformTest", JavaExec) {
     enableAssertions = true
 }
 
+tasks.register("runnerPlatformBenchmark", JavaExec) {
+    mainClass = 
"org.apache.ignite.internal.runner.app.PlatformBenchmarkNodeRunner"
+
+    classpath = sourceSets.integrationTest.runtimeClasspath
+
+    enableAssertions = true
+}
+
 integrationTest {
     useJUnitPlatform {
         if (System.getProperty("sqlTest") != null) {
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java
new file mode 100644
index 0000000000..8aa6b2fcfc
--- /dev/null
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.runner.app;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.client.handler.ClientHandlerMetricSource;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
+
+/**
+ * Helper class for non-Java platform benchmarks (.NET, C++, Python, ...).
+ */
+public class PlatformBenchmarkNodeRunner {
+    /** Test node name. */
+    private static final String NODE_NAME = 
PlatformBenchmarkNodeRunner.class.getCanonicalName();
+
+    /** Nodes bootstrap configuration. */
+    private static final Map<String, String> nodesBootstrapCfg = Map.of(
+            NODE_NAME, "{\n"
+                    + "  \"clientConnector\":{\"port\": 
10420,\"portRange\":1,\"idleTimeout\":999000},"
+                    + "  \"network\": {\n"
+                    + "    \"port\":3344,\n"
+                    + "    \"nodeFinder\": {\n"
+                    + "      \"netClusterNodes\":[ \"localhost:3344\" ]\n"
+                    + "    }\n"
+                    + "  }\n"
+                    + "}"
+    );
+
+    /** Base path for all temporary folders. */
+    private static final Path BASE_PATH = Path.of("target", "work", 
"PlatformBenchmarkNodeRunner");
+
+    /**
+     * Entry point.
+     *
+     * @param args Args.
+     */
+    public static void main(String[] args) throws Exception {
+        System.out.println("Starting benchmark node runner...");
+
+        List<Ignite> startedNodes = 
PlatformTestNodeRunner.startNodes(BASE_PATH, nodesBootstrapCfg);
+
+        Object clientHandlerModule = 
IgniteTestUtils.getFieldValue(startedNodes.get(0), "clientHandlerModule");
+        ClientHandlerMetricSource metrics = 
IgniteTestUtils.getFieldValue(clientHandlerModule, "metrics");
+        metrics.enable();
+
+        while (true) {
+            Thread.sleep(3000);
+            System.out.println();
+            System.out.println("sessionsActive: " + metrics.sessionsActive());
+            System.out.println("sessionsAccepted: " + 
metrics.sessionsAccepted());
+            System.out.println("requestsActive: " + metrics.requestsActive());
+            System.out.println("requestsProcessed: " + 
metrics.requestsProcessed());
+        }
+    }
+}
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
index ae91f6f957..a3ee9a833c 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
@@ -22,6 +22,7 @@ import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.await;
 import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.escapeWindowsPath;
 import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.getResourcePath;
 
+import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -53,6 +54,7 @@ import 
org.apache.ignite.internal.table.impl.DummySchemaManagerImpl;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.sql.Session;
 import org.apache.ignite.table.Tuple;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Helper class for non-Java platform tests (.NET, C++, Python, ...). Starts 
nodes, populates tables and data for tests.
@@ -179,14 +181,44 @@ public class PlatformTestNodeRunner {
             return;
         }
 
-        IgniteUtils.deleteIfExists(BASE_PATH);
-        Files.createDirectories(BASE_PATH);
+        List<Ignite> startedNodes = startNodes(BASE_PATH, nodesBootstrapCfg);
+
+        createTables(startedNodes.get(0));
+
+        String ports = startedNodes.stream()
+                .map(n -> String.valueOf(getPort((IgniteImpl) n)))
+                .collect(Collectors.joining(","));
+
+        System.out.println("THIN_CLIENT_PORTS=" + ports);
+
+        long runTimeMinutes = getRunTimeMinutes();
+        System.out.println("Nodes will be active for " + runTimeMinutes + " 
minutes.");
+
+        Thread.sleep(runTimeMinutes * 60_000);
+        System.out.println("Exiting after " + runTimeMinutes + " minutes.");
+
+        for (Ignite node : startedNodes) {
+            IgnitionManager.stop(node.name());
+        }
+    }
+
+    /**
+     * Start nodes.
+     *
+     * @param basePath Base path.
+     * @param nodeCfg Node configuration.
+     * @return Started nodes.
+     */
+    @NotNull
+    static List<Ignite> startNodes(Path basePath, Map<String, String> nodeCfg) 
throws IOException {
+        IgniteUtils.deleteIfExists(basePath);
+        Files.createDirectories(basePath);
 
         var sslPassword = "123456";
         var trustStorePath = 
escapeWindowsPath(getResourcePath(PlatformTestNodeRunner.class, 
"ssl/trust.jks"));
         var keyStorePath = 
escapeWindowsPath(getResourcePath(PlatformTestNodeRunner.class, 
"ssl/server.jks"));
 
-        List<CompletableFuture<Ignite>> igniteFutures = 
nodesBootstrapCfg.entrySet().stream()
+        List<CompletableFuture<Ignite>> igniteFutures = 
nodeCfg.entrySet().stream()
                 .map(e -> {
                     String nodeName = e.getKey();
                     String config = e.getValue()
@@ -194,11 +226,11 @@ public class PlatformTestNodeRunner {
                             .replace("TRUSTSTORE_PATH", trustStorePath)
                             .replace("SSL_STORE_PASS", sslPassword);
 
-                    return IgnitionManager.start(nodeName, config, 
BASE_PATH.resolve(nodeName));
+                    return IgnitionManager.start(nodeName, config, 
basePath.resolve(nodeName));
                 })
                 .collect(toList());
 
-        String metaStorageNodeName = 
nodesBootstrapCfg.keySet().iterator().next();
+        String metaStorageNodeName = nodeCfg.keySet().iterator().next();
 
         InitParameters initParameters = InitParameters.builder()
                 .destinationNodeName(metaStorageNodeName)
@@ -213,23 +245,7 @@ public class PlatformTestNodeRunner {
 
         System.out.println("Ignite nodes started");
 
-        createTables(startedNodes.get(0));
-
-        String ports = startedNodes.stream()
-                .map(n -> String.valueOf(getPort((IgniteImpl) n)))
-                .collect(Collectors.joining(","));
-
-        System.out.println("THIN_CLIENT_PORTS=" + ports);
-
-        long runTimeMinutes = getRunTimeMinutes();
-        System.out.println("Nodes will be active for " + runTimeMinutes + " 
minutes.");
-
-        Thread.sleep(runTimeMinutes * 60_000);
-        System.out.println("Exiting after " + runTimeMinutes + " minutes.");
-
-        for (Ignite node : startedNodes) {
-            IgnitionManager.stop(node.name());
-        }
+        return startedNodes;
     }
 
     private static void createTables(Ignite node) {

Reply via email to