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

apkhmv 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 958e3c7f33 IGNITE-18604 Fix memory leak (#1749)
958e3c7f33 is described below

commit 958e3c7f33eddbcd59c3a8904b82a5e860fbd37f
Author: Mikhail <[email protected]>
AuthorDate: Mon Mar 6 21:14:05 2023 +0300

    IGNITE-18604 Fix memory leak (#1749)
    
    
    ---------
    
    Co-authored-by: Mikhail Pochatkin <[email protected]>
    Co-authored-by: Vadim Pakhnushev <[email protected]>
---
 .../apache/ignite/internal/rest/RestFactory.java    | 11 +++++++++++
 .../apache/ignite/internal/rest/RestComponent.java  | 11 ++++++-----
 .../internal/rest/RestFactoriesDestroyer.java}      | 21 +++++++++++++++++++--
 .../rest/authentication/AuthProviderFactory.java    |  7 ++++++-
 .../rest/cluster/ClusterManagementRestFactory.java  | 10 ++++++++--
 .../rest/configuration/PresentationsFactory.java    | 10 ++++++++--
 .../rest/deployment/CodeDeploymentRestFactory.java  |  7 ++++++-
 .../internal/rest/metrics/MetricRestFactory.java    |  7 ++++++-
 .../rest/node/NodeManagementRestFactory.java        | 10 ++++++++--
 .../org/apache/ignite/internal/app/IgniteImpl.java  | 14 ++++++++------
 10 files changed, 86 insertions(+), 22 deletions(-)

diff --git 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
index 88567cc8b6..6926a2e093 100644
--- 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
+++ 
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
@@ -17,8 +17,19 @@
 
 package org.apache.ignite.internal.rest;
 
+import io.micronaut.runtime.Micronaut;
+
 /**
  * Factory that produces all beans that is necessary for the controller class.
  */
 public interface RestFactory {
+    /**
+     * Destroy method. All resources of the factory implementation must be 
cleaned and all fields must be set to {@code null}.
+     *      The reason of these requirements is Micronaut design.
+     *      {@link Micronaut#start()} store shutdown hook and capture a pointer
+     *      to the embedded application {@link 
io.micronaut.http.server.netty.NettyEmbeddedServer} and as a result
+     *      {@link io.micronaut.context.ApplicationContext} will never be 
collected by the GC.
+     *      All rest factories stored in the application context should be 
cleaned to prevent memory leak.
+     */
+    void cleanResources();
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
index 9d0e57b1e0..7aa8b5bde0 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestComponent.java
@@ -31,6 +31,7 @@ import java.net.UnknownHostException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.manager.IgniteComponent;
@@ -61,7 +62,7 @@ public class RestComponent implements IgniteComponent {
     private static final IgniteLogger LOG = 
Loggers.forClass(RestComponent.class);
 
     /** Factories that produce beans needed for REST controllers. */
-    private final List<RestFactory> restFactories;
+    private final List<Supplier<RestFactory>> restFactories;
 
     private final RestConfiguration restConfiguration;
 
@@ -77,7 +78,7 @@ public class RestComponent implements IgniteComponent {
     /**
      * Creates a new instance of REST module.
      */
-    public RestComponent(List<RestFactory> restFactories, RestConfiguration 
restConfiguration) {
+    public RestComponent(List<Supplier<RestFactory>> restFactories, 
RestConfiguration restConfiguration) {
         this.restFactories = restFactories;
         this.restConfiguration = restConfiguration;
     }
@@ -180,17 +181,17 @@ public class RestComponent implements IgniteComponent {
         return micronaut
                 .properties(properties)
                 .banner(false)
-                .mapError(ServerStartupException.class, 
this::mapServerStartupException)
+                .mapError(ServerStartupException.class, 
RestComponent::mapServerStartupException)
                 .mapError(ApplicationStartupException.class, ex -> -1);
     }
 
     private void setFactories(Micronaut micronaut) {
         for (var factory : restFactories) {
-            micronaut.singletons(factory);
+            micronaut.singletons(factory.get());
         }
     }
 
-    private int mapServerStartupException(ServerStartupException exception) {
+    private static int mapServerStartupException(ServerStartupException 
exception) {
         if (exception.getCause() instanceof BindException) {
             return -1; // -1 forces the micronaut to throw an 
ApplicationStartupException
         } else {
diff --git 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestFactoriesDestroyer.java
similarity index 52%
copy from 
modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
copy to 
modules/rest/src/main/java/org/apache/ignite/internal/rest/RestFactoriesDestroyer.java
index 88567cc8b6..58051f6ccb 100644
--- 
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/RestFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/RestFactoriesDestroyer.java
@@ -17,8 +17,25 @@
 
 package org.apache.ignite.internal.rest;
 
+import io.micronaut.context.event.BeanDestroyedEvent;
+import io.micronaut.context.event.BeanDestroyedEventListener;
+import jakarta.inject.Singleton;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+
 /**
- * Factory that produces all beans that is necessary for the controller class.
+ * Destroyer of any rest factory {@link RestFactory}.
  */
-public interface RestFactory {
+@Singleton
+public class RestFactoriesDestroyer implements 
BeanDestroyedEventListener<RestFactory> {
+    private static final IgniteLogger LOG = 
Loggers.forClass(RestFactoriesDestroyer.class);
+
+    @Override
+    public void onDestroyed(BeanDestroyedEvent<RestFactory> event) {
+        RestFactory bean = event.getBean();
+        if (bean != null) {
+            LOG.debug("Destroy rest factory " + bean);
+            bean.cleanResources();
+        }
+    }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/AuthProviderFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/AuthProviderFactory.java
index 905e611bba..c01664a147 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/AuthProviderFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/AuthProviderFactory.java
@@ -30,7 +30,7 @@ import org.apache.ignite.internal.rest.RestFactory;
 @Factory
 public class AuthProviderFactory implements RestFactory {
 
-    private final DelegatingAuthenticationProvider authenticationProvider;
+    private DelegatingAuthenticationProvider authenticationProvider;
 
     public AuthProviderFactory(AuthenticationConfiguration configuration) {
         this.authenticationProvider = new DelegatingAuthenticationProvider();
@@ -47,4 +47,9 @@ public class AuthProviderFactory implements RestFactory {
     public DelegatingAuthenticationProvider authenticationProvider() {
         return authenticationProvider;
     }
+
+    @Override
+    public void cleanResources() {
+        authenticationProvider = null;
+    }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/cluster/ClusterManagementRestFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/cluster/ClusterManagementRestFactory.java
index 8a47cedd72..b724d2269a 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/cluster/ClusterManagementRestFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/cluster/ClusterManagementRestFactory.java
@@ -31,9 +31,9 @@ import org.apache.ignite.network.TopologyService;
  */
 @Factory
 public class ClusterManagementRestFactory implements RestFactory {
-    private final ClusterService clusterService;
+    private ClusterService clusterService;
 
-    private final ClusterManagementGroupManager cmgManager;
+    private ClusterManagementGroupManager cmgManager;
 
     /** Constructor. */
     public ClusterManagementRestFactory(ClusterService clusterService, 
ClusterManagementGroupManager cmgManager) {
@@ -58,4 +58,10 @@ public class ClusterManagementRestFactory implements 
RestFactory {
     public TopologyService topologyService() {
         return clusterService.topologyService();
     }
+
+    @Override
+    public void cleanResources() {
+        clusterService = null;
+        cmgManager = null;
+    }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/PresentationsFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/PresentationsFactory.java
index c422ff55a4..371d436911 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/PresentationsFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/configuration/PresentationsFactory.java
@@ -30,8 +30,8 @@ import 
org.apache.ignite.internal.rest.configuration.hocon.HoconPresentation;
  */
 @Factory
 public class PresentationsFactory implements RestFactory {
-    private final ConfigurationPresentation<String> nodeCfgPresentation;
-    private final ConfigurationPresentation<String> clusterCfgPresentation;
+    private ConfigurationPresentation<String> nodeCfgPresentation;
+    private ConfigurationPresentation<String> clusterCfgPresentation;
 
     public PresentationsFactory(ConfigurationManager nodeCfgMgr, 
ConfigurationManager clusterCfgMgr) {
         this.nodeCfgPresentation = new 
HoconPresentation(nodeCfgMgr.configurationRegistry());
@@ -51,4 +51,10 @@ public class PresentationsFactory implements RestFactory {
     public ConfigurationPresentation<String> nodeCfgPresentation() {
         return nodeCfgPresentation;
     }
+
+    @Override
+    public void cleanResources() {
+        nodeCfgPresentation = null;
+        clusterCfgPresentation = null;
+    }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/CodeDeploymentRestFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/CodeDeploymentRestFactory.java
index a75c167f2c..d4411a0618 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/CodeDeploymentRestFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/CodeDeploymentRestFactory.java
@@ -28,7 +28,7 @@ import org.apache.ignite.internal.rest.RestFactory;
  */
 @Factory
 public class CodeDeploymentRestFactory implements RestFactory {
-    private final IgniteDeployment igniteDeployment;
+    private IgniteDeployment igniteDeployment;
 
     public CodeDeploymentRestFactory(IgniteDeployment igniteDeployment) {
         this.igniteDeployment = igniteDeployment;
@@ -39,4 +39,9 @@ public class CodeDeploymentRestFactory implements RestFactory 
{
     public IgniteDeployment deployment() {
         return igniteDeployment;
     }
+
+    @Override
+    public void cleanResources() {
+        igniteDeployment = null;
+    }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/metrics/MetricRestFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/metrics/MetricRestFactory.java
index 38bf0b44f4..e93d616240 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/metrics/MetricRestFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/metrics/MetricRestFactory.java
@@ -28,7 +28,7 @@ import org.apache.ignite.internal.rest.RestFactory;
  */
 @Factory
 public class MetricRestFactory implements RestFactory {
-    private final MetricManager metricManager;
+    private MetricManager metricManager;
 
     public MetricRestFactory(MetricManager metricManager) {
         this.metricManager = metricManager;
@@ -39,4 +39,9 @@ public class MetricRestFactory implements RestFactory {
     public MetricManager metricManager() {
         return metricManager;
     }
+
+    @Override
+    public void cleanResources() {
+        metricManager = null;
+    }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementRestFactory.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementRestFactory.java
index 21997ff5d5..344103a7a5 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementRestFactory.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementRestFactory.java
@@ -27,9 +27,9 @@ import org.apache.ignite.internal.rest.RestFactory;
  */
 @Factory
 public class NodeManagementRestFactory implements RestFactory {
-    private final StateProvider stateProvider;
+    private StateProvider stateProvider;
 
-    private final NameProvider nameProvider;
+    private NameProvider nameProvider;
 
     public NodeManagementRestFactory(StateProvider stateProvider, NameProvider 
nameProvider) {
         this.stateProvider = stateProvider;
@@ -47,4 +47,10 @@ public class NodeManagementRestFactory implements 
RestFactory {
     public NameProvider nameProvider() {
         return nameProvider;
     }
+
+    @Override
+    public void cleanResources() {
+        stateProvider = null;
+        nameProvider = null;
+    }
 }
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java 
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index ff05278575..f579c56f2f 100644
--- 
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -38,6 +38,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.Supplier;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgnitionManager;
 import org.apache.ignite.client.handler.ClientHandlerModule;
@@ -525,12 +526,13 @@ public class IgniteImpl implements Ignite {
         AuthenticationConfiguration authConfiguration = 
clusterCfgMgr.configurationRegistry()
                 .getConfiguration(SecurityConfiguration.KEY)
                 .authentication();
-        RestFactory presentationsFactory = new 
PresentationsFactory(nodeCfgMgr, clusterCfgMgr);
-        RestFactory clusterManagementRestFactory = new 
ClusterManagementRestFactory(clusterSvc, cmgMgr);
-        RestFactory nodeManagementRestFactory = new 
NodeManagementRestFactory(lifecycleManager, () -> name);
-        RestFactory nodeMetricRestFactory = new 
MetricRestFactory(metricManager);
-        AuthProviderFactory authProviderFactory = new 
AuthProviderFactory(authConfiguration);
-        RestFactory deploymentCodeRestFactory = new 
CodeDeploymentRestFactory(deploymentManager);
+
+        Supplier<RestFactory> presentationsFactory = () -> new 
PresentationsFactory(nodeCfgMgr, clusterCfgMgr);
+        Supplier<RestFactory> clusterManagementRestFactory = () -> new 
ClusterManagementRestFactory(clusterSvc, cmgMgr);
+        Supplier<RestFactory> nodeManagementRestFactory = () -> new 
NodeManagementRestFactory(lifecycleManager, () -> name);
+        Supplier<RestFactory> nodeMetricRestFactory = () -> new 
MetricRestFactory(metricManager);
+        Supplier<RestFactory> authProviderFactory = () -> new 
AuthProviderFactory(authConfiguration);
+        Supplier<RestFactory> deploymentCodeRestFactory = () -> new 
CodeDeploymentRestFactory(deploymentManager);
         RestConfiguration restConfiguration = 
nodeCfgMgr.configurationRegistry().getConfiguration(RestConfiguration.KEY);
         return new RestComponent(
                 List.of(presentationsFactory,

Reply via email to