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

nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new ef685f5c312 IGNITE-28243 Keep service descriptor even if no 
deployments (#12910)
ef685f5c312 is described below

commit ef685f5c3121701c21fd4f8023c48b2417ad5f52
Author: Nikolay <[email protected]>
AuthorDate: Thu Mar 19 14:35:35 2026 +0300

    IGNITE-28243 Keep service descriptor even if no deployments (#12910)
    
    Some users implements the following scenario:
    
    1. Starts sever node (configuration node), first, with the service
    configuration. NodeFilter like `n -> n.isClient()`.
    2. Starts client nodes that hosts service. Count of the clients depends
    on the expected workload.
    
    This case was broken in the 66a4a2d876aad528ee1be35d8711441ecdfdf97b
    This PR fix regression.
---
 .../processors/service/IgniteServiceProcessor.java | 10 ---
 .../IgniteServiceDeployOnJoinedNodeTest.java       | 94 ++++++++++++++++++++++
 .../IgniteServiceDeploymentFailureTest.java        | 31 ++++---
 .../testsuites/IgniteServiceGridTestSuite.java     |  2 +
 4 files changed, 118 insertions(+), 19 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
index 287f36c1262..4cf6bd74c26 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
@@ -1579,16 +1579,6 @@ public class IgniteServiceProcessor extends 
GridProcessorAdapter implements Igni
 
         try {
             updateServicesMap(deployedServices, fullTops);
-
-            for (Map.Entry<IgniteUuid, Map<UUID, Integer>> e : 
fullTops.entrySet()) {
-                // Checking if there are successful deployments.
-                // If none, service not deployed and must be removed from 
descriptors.
-                if (e.getValue().entrySet().stream().allMatch(nodeTop -> 
nodeTop.getValue() == 0)) {
-                    removeFromServicesMap(registeredServices, 
registeredServicesByName, e.getKey());
-
-                    removeFromServicesMap(deployedServices, 
deployedServicesByName, e.getKey());
-                }
-            }
         }
         finally {
             leaveBusy();
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeployOnJoinedNodeTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeployOnJoinedNodeTest.java
new file mode 100644
index 00000000000..80b604c40da
--- /dev/null
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeployOnJoinedNodeTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.processors.service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Supplier;
+import org.apache.ignite.cluster.ClusterGroup;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceConfiguration;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Tests check the following case:
+ * 1. Node "A" starts with the service configuration. But, NodeFilter filters 
out node "A".
+ * 2. Node "B" starts and NodeFilter conforms it.
+ * 3. It is expected that service will be deployed on node "B".
+ */
+@RunWith(Parameterized.class)
+public class IgniteServiceDeployOnJoinedNodeTest extends 
GridCommonAbstractTest {
+    /** */
+    @Parameterized.Parameter
+    public boolean nodeBClient;
+
+    /** @return Test parameters. */
+    @Parameterized.Parameters(name = "nodeBClient={0}")
+    public static Collection<?> parameters() {
+        return List.of(false, true);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName)
+            .setConsistentId(igniteInstanceName)
+            .setClientMode("B".equals(igniteInstanceName) && nodeBClient);
+
+        if (cfg.getConsistentId().equals("A")) {
+            // Server node stores service config.
+            cfg = cfg.setServiceConfiguration(new ServiceConfiguration()
+                .setName("service")
+                .setService(new GreeterService())
+                .setMaxPerNodeCount(1)
+                // Service deployed on node "B", only.
+                .setNodeFilter(n -> n.consistentId().equals("B"))
+            );
+        }
+
+        return cfg;
+    }
+
+    /** */
+    @Test
+    public void test() throws Exception {
+        // Node A only stores service config.
+        try (IgniteEx a = startGrid("A")) {
+            // Service must be deployed on node B.
+            try (IgniteEx b = startGrid("B")) {
+                assertEquals(b.configuration().isClientMode(), 
(Boolean)nodeBClient);
+
+                ClusterGroup grp = nodeBClient ? b.cluster().forClients() : 
b.cluster().forServers();
+
+                assertEquals("Hello", b.services(grp).serviceProxy("service", 
Supplier.class, false).get());
+            }
+        }
+    }
+
+    /** */
+    private static class GreeterService implements Supplier<String>, Service {
+        /** {@inheritDoc} */
+        @Override public String get() {
+            return "Hello";
+        }
+    }
+}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeploymentFailureTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeploymentFailureTest.java
index 1263cac4f1c..da9cc76b7d8 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeploymentFailureTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/IgniteServiceDeploymentFailureTest.java
@@ -20,7 +20,6 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.UUID;
 import java.util.function.Predicate;
-
 import org.apache.ignite.Ignite;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.cluster.ClusterNode;
@@ -30,6 +29,7 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteServicesImpl;
 import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.resources.IgniteInstanceResource;
 import org.apache.ignite.services.Service;
 import org.apache.ignite.services.ServiceConfiguration;
 import org.apache.ignite.services.ServiceDeploymentException;
@@ -179,7 +179,7 @@ public class IgniteServiceDeploymentFailureTest extends 
GridCommonAbstractTest {
 
         assertTrue(
             FAILED_SERVICE_SHOULD_NOT_BE_PRESENT_IN_THE_CLUSTER,
-            waitForCondition(() -> 
noDescriptorInClusterForService(INIT_THROWING_SERVICE_NAME), TIMEOUT)
+            waitForCondition(() -> totalInstancesCount(client, 
INIT_THROWING_SERVICE_NAME) == 0, TIMEOUT)
         );
 
         assertEquals(
@@ -226,19 +226,27 @@ public class IgniteServiceDeploymentFailureTest extends 
GridCommonAbstractTest {
                 new ServiceConfiguration()
                     .setName(INIT_THROWING_SERVICE_NAME)
                     .setService(new InitThrowingService())
+                    .setMaxPerNodeCount(2)
                     .setTotalCount(20)
             )
         );
 
         assertTrue(
-            FAILED_SERVICE_SHOULD_NOT_BE_PRESENT_IN_THE_CLUSTER,
-            waitForCondition(() -> 
noDescriptorInClusterForService(INIT_THROWING_SERVICE_NAME), TIMEOUT)
+            DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER,
+            waitForCondition(() -> noopSrvcTotalCnt == 
totalInstancesCount(ign, NOOP_SERVICE_NAME), TIMEOUT)
         );
 
-        assertEquals(
-            DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER,
-            noopSrvcTotalCnt,
-            totalInstancesCount(ign, NOOP_SERVICE_NAME)
+        assertTrue(
+            "Service must fail to init on all nodes",
+            totalInstancesCount(ign, INIT_THROWING_SERVICE_NAME) == 0
+        );
+
+        // Must start one instance of InitThrowingService
+        startGrid(getConfiguration("deploy-node"));
+
+        assertTrue(
+            "Service must be deployed on new deploy-node",
+            waitForCondition(() -> totalInstancesCount(ign, 
INIT_THROWING_SERVICE_NAME) == 2, TIMEOUT)
         );
     }
 
@@ -308,9 +316,14 @@ public class IgniteServiceDeploymentFailureTest extends 
GridCommonAbstractTest {
      * Service that throws an exception in init.
      */
     private static class InitThrowingService implements Service {
+        /** */
+        @IgniteInstanceResource
+        private IgniteEx ignite;
+
         /** {@inheritDoc} */
         @Override public void init() throws Exception {
-            throw new Exception("Service init exception");
+            if 
(!"deploy-node".equals(ignite.configuration().getIgniteInstanceName()))
+                throw new Exception("Service init exception");
         }
     }
 
diff --git 
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java
 
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java
index 373182884d9..514efaa7681 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteServiceGridTestSuite.java
@@ -40,6 +40,7 @@ import 
org.apache.ignite.internal.processors.service.GridServiceReassignmentSelf
 import 
org.apache.ignite.internal.processors.service.GridServiceSerializationSelfTest;
 import 
org.apache.ignite.internal.processors.service.IgniteServiceCallContextTest;
 import 
org.apache.ignite.internal.processors.service.IgniteServiceCallInterceptorTest;
+import 
org.apache.ignite.internal.processors.service.IgniteServiceDeployOnJoinedNodeTest;
 import 
org.apache.ignite.internal.processors.service.IgniteServiceDeployment2ClassLoadersDefaultMarshallerTest;
 import 
org.apache.ignite.internal.processors.service.IgniteServiceDeploymentClassLoadingDefaultMarshallerTest;
 import 
org.apache.ignite.internal.processors.service.IgniteServiceDeploymentFailureTest;
@@ -101,6 +102,7 @@ import org.junit.runners.Suite;
     IgniteServiceDeploymentClassLoadingDefaultMarshallerTest.class,
     IgniteServiceDeployment2ClassLoadersDefaultMarshallerTest.class,
     IgniteServiceDeploymentFailureTest.class,
+    IgniteServiceDeployOnJoinedNodeTest.class,
 
     GridServiceExceptionPropagationTest.class,
     ServiceDeploymentProcessingOnCoordinatorLeftTest.class,

Reply via email to