Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IGNITE-23226 Services: deployment failed but service descriptor stay #11634

Merged
merged 49 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f785deb
IGNITE-23226 Copied IgniteServiceDeployUnknownClassTest from JIRA tic…
vladnovoren Oct 28, 2024
ea36c42
IGNITE-23226 Added test for ServiceDeploymentException when Server.in…
vladnovoren Oct 30, 2024
2bb3acb
IGNITE-23226 Added test for clear server service descriptors.
vladnovoren Nov 1, 2024
19b8f90
IGNITE-23226 Descriptors test for client and server
vladnovoren Nov 6, 2024
7a89397
IGNITE-23226 Merged tests for desccriptors on client and server into …
vladnovoren Nov 6, 2024
7f0ae60
IGNITE-23226 New reproducer that deploys properly working NoopService…
vladnovoren Nov 8, 2024
cee60f8
IGNITE-23226 Cleared registered services descriptors for services wit…
vladnovoren Nov 8, 2024
026f7cf
IGNITE-23226 Now we clear only registered services descriptors - in I…
vladnovoren Nov 8, 2024
39efe93
IGNITE-23226 Removed unused field IgniteServiceDeploymentFailureTest#…
vladnovoren Nov 9, 2024
1ab8ec0
IGNITE-23226 Some codestyle fixes.
vladnovoren Nov 10, 2024
aa7ad65
IGNITE-23558 Minor code cleanup. (#11555)
Vladsz83 Oct 30, 2024
7753855
IGNITE-23535 Fix IgniteUtilsUnitTest.shouldNotProduceWarningsWhenClos…
vladnovoren Oct 30, 2024
5fd9581
IGNITE-23563 Fix node fails while checking security permission for ac…
ivandasch Oct 31, 2024
0aab095
IGNITE-23584 Refactored snapshot quick verify handler. (#11632)
Vladsz83 Nov 1, 2024
ffd2aad
IGNITE-23408 SQL Calcite: Fix 'typeof' function behaviour when operan…
Vladsz83 Nov 1, 2024
f865e6c
IGNITE-23593 Use CacheQuery instead of GridCacheQueryAdapter (#11639)
nizhikov Nov 2, 2024
f77dca1
IGNITE-23554 ScanQuery code cleanup (#11645)
nizhikov Nov 7, 2024
96aebb1
IGNITE-23591 Fix entry and checkpoint locks orders in DataStreamer an…
timoninmaxim Nov 7, 2024
c70dad7
IGNITE-23557 SQL Calcite: Fix assertion in COUNT(DISTINCT) accumulato…
Vladsz83 Nov 8, 2024
3d19f71
IGNITE-23516 SQL Calcite: Fix rounding of numerics (unification for D…
Vladsz83 Nov 8, 2024
30e3a1b
Merge branch 'master' into ignite-23226
vladnovoren Nov 10, 2024
65f4ee9
IGNITE-23226 Added license header for IgniteServiceDeploymentFailureT…
vladnovoren Nov 10, 2024
3dff745
IGNITE-23226 Added waitForCondition to check descriptors.
vladnovoren Nov 11, 2024
b3a1887
IGNITE-23226 Removed unnecessary get call and snapshot update.
vladnovoren Nov 11, 2024
f46f1c5
IGNITE-23226 Some review fixes.
vladnovoren Nov 11, 2024
5c07699
IGNITE-23226 Simplified grids creation and iteration.
vladnovoren Nov 11, 2024
3836ca4
IGNITE-23226 Some review fixes.
vladnovoren Nov 11, 2024
8428fa9
IGNITE-23226 Some review fixes: codestyle & waitForCondition.
vladnovoren Nov 11, 2024
604df0b
IGNITE-23226 Removed unused import
vladnovoren Nov 11, 2024
58822bb
IGNITE-23226 Check for whole cluster in testFailWhenClassNotFound & a…
vladnovoren Nov 11, 2024
c7866f7
IGNITE-23226 Checkstyle fixes.
vladnovoren Nov 11, 2024
294e1b4
IGNITE-23226 totalInstancesCount.
vladnovoren Nov 12, 2024
43d41be
IGNITE-23226 Test for static configuration.
vladnovoren Nov 12, 2024
d2b6225
IGNITE-23226 Minor review fixes + added some client nodes in static c…
vladnovoren Nov 12, 2024
c1e3b1a
IGNITE-23226 Continuation indent: 8 -> 4.
vladnovoren Nov 12, 2024
acf9ce1
IGNITE-23226 Line breaks.
vladnovoren Nov 12, 2024
79f289a
IGNITE-23226 Codestyle & unnecessary fields usage.
vladnovoren Nov 12, 2024
ae9bd6f
IGNITE-23226 Codestyle fixes.
vladnovoren Nov 13, 2024
5eaec16
IGNITE-23578 Fix removal of required affinity assignment instance fro…
alex-plekhanov Nov 12, 2024
cea8fe8
IGNITE-23620 Remove excessive "toString" occurrences (#11653)
AlexLev-Tor Nov 13, 2024
77761ec
IGNITE-23394 [ducktests] Migrate to ducktape 0.12.0 (#11652)
skorotkov Nov 13, 2024
c5ec2f2
Merge branch 'master' into ignite-23226
vladnovoren Nov 13, 2024
d23b66c
IGNITE-23226 Fixed IgniteServiceCallInterceptorTest - also clear Igni…
vladnovoren Nov 16, 2024
a408e84
IGNITE-23226 Added removeRegisteredServiceRecord & removeDeployedServ…
vladnovoren Nov 17, 2024
e28b554
save
vladnovoren Nov 18, 2024
cc3b714
IGNITE-23226 Total instances count check.
vladnovoren Nov 19, 2024
52a75fe
IGNITE-23226 Refactored removeServiceRecord -> removeFromServiceMap a…
vladnovoren Nov 19, 2024
7b1b13f
IGNITE-23226 Minor fixes.
vladnovoren Nov 19, 2024
165f9d6
IGNITE-23226 Codestyle fixes.
vladnovoren Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,16 @@ void updateServicesTopologies(@NotNull final Map<IgniteUuid, Map<UUID, Integer>>

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();
Expand Down Expand Up @@ -1631,11 +1641,9 @@ void updateDeployedServices(final ServiceDeploymentActions depActions) {
});

depActions.servicesToUndeploy().forEach((srvcId, desc) -> {
ServiceInfo rmv = deployedServices.remove(srvcId);
ServiceInfo rmv = removeFromServicesMap(deployedServices, deployedServicesByName, srvcId);

assert rmv != null && rmv == desc : "Concurrent map modification.";

deployedServicesByName.remove(rmv.name());
assert rmv == desc : "Concurrent map modification.";
});
}
finally {
Expand Down Expand Up @@ -1862,8 +1870,7 @@ else if (req instanceof ServiceUndeploymentRequest)
}
}
else if (req instanceof ServiceUndeploymentRequest) {
ServiceInfo rmv = registeredServices.remove(reqSrvcId);
registeredServicesByName.remove(oldDesc.name());
ServiceInfo rmv = removeFromServicesMap(registeredServices, registeredServicesByName, reqSrvcId);

assert oldDesc == rmv : "Concurrent map modification.";

Expand Down Expand Up @@ -2014,6 +2021,29 @@ private void updateServicesMap(Map<IgniteUuid, ServiceInfo> services,
});
}

/**
* Remove service record from service map and corresponding services by name map.
*
* @param srvcsMap Services map.
* @param srvcsByNameMap Services by name map.
* @param srvcId Service id.
*
* @return Removed service descriptor.
* */
private ServiceInfo removeFromServicesMap(
Map<IgniteUuid, ServiceInfo> srvcsMap,
Map<String, ServiceInfo> srvcsByNameMap,
IgniteUuid srvcId
) {
ServiceInfo desc = srvcsMap.remove(srvcId);

assert desc != null : "Concurrent map modification.";

srvcsByNameMap.remove(desc.name());

return desc;
}

/**
* Enters busy state.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
/*
* 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.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;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.failure.StopNodeFailureHandler;
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.services.Service;
import org.apache.ignite.services.ServiceConfiguration;
import org.apache.ignite.services.ServiceDeploymentException;
import org.apache.ignite.services.ServiceDescriptor;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;

import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
import static org.apache.ignite.testframework.GridTestUtils.getFieldValue;
import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;

/**
* Test class for verifying the behavior of Ignite service deployment when
* specified service classes are not found or when service initialization fails.
*/
public class IgniteServiceDeploymentFailureTest extends GridCommonAbstractTest {
/** */
private static final String NODE_FILTER_CLS_NAME = "org.apache.ignite.tests.p2p.ExcludeNodeFilter";

/** */
private static final String NOOP_SERVICE_NAME = "NoopService";

/** */
private static final String INIT_THROWING_SERVICE_NAME = "InitThrowingService";

/** */
private static final int SERVER_NODES_CNT = 5;

/** */
private static final int CLIENT_NODES_CNT = 4;

/** */
private static final int TIMEOUT = 10_000;

/** */
private static final String DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER = "Deployed service must be presented in cluster";

/** */
private static final String FAILED_SERVICE_SHOULD_NOT_BE_PRESENT_IN_THE_CLUSTER =
"Service descriptors whose deployment has failed should not be present in the cluster";

/** */
public static final String REGISTERED_SERVICES_BY_NAME = "registeredServicesByName";

/** */
public static final String DEPLOYED_SERVICES = "deployedServices";

/** */
public static final String DEPLOYED_SERVICES_BY_NAME = "deployedServicesByName";

/** */
private static ClassLoader extClsLdr;

/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);

cfg.setPeerClassLoadingEnabled(false);
cfg.setFailureHandler(new StopNodeFailureHandler());

return cfg;
}

/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();

extClsLdr = getExternalClassLoader();
}

/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();

super.afterTest();

extClsLdr = null;
}

/**
* Tests that deploying a service with a missing class causes a ServiceDeploymentException.
*
* @throws Exception If failed.
*/
@Test
public void testFailWhenClassNotFound() throws Exception {
startGrid();

IgniteEx cli = startClientGrid(1);

ServiceConfiguration svcCfg = new ServiceConfiguration()
.setName(NOOP_SERVICE_NAME)
.setService(new NoopService())
.setNodeFilter(((Class<IgnitePredicate<ClusterNode>>)extClsLdr.loadClass(NODE_FILTER_CLS_NAME))
.getConstructor(UUID.class)
.newInstance(cli.configuration().getNodeId()))
.setTotalCount(1);

assertThrowsWithCause(() -> cli.services().deploy(svcCfg), ServiceDeploymentException.class);

assertTrue(waitForCondition(() -> noDescriptorInClusterForService(NOOP_SERVICE_NAME), TIMEOUT));
}

/**
* Tests that service descriptors are clear after an attempt of deploying a service which throws an exception
* during initialization. Dynamic configuration for services is used.
*
* @throws Exception If failed.
*/
@Test
public void testFailedServiceDescriptorsDynamicConfiguration() throws Exception {
final int maxNoopSrvcMaxPerNodeCnt = 4;

startGrids(SERVER_NODES_CNT);

IgniteEx client = startClientGrid(SERVER_NODES_CNT);

for (int i = 1; i < CLIENT_NODES_CNT; i++)
startClientGrid(SERVER_NODES_CNT + i);

ServiceConfiguration noopSrvcCfg = new ServiceConfiguration()
.setName(NOOP_SERVICE_NAME)
.setService(new NoopService())
.setTotalCount(SERVER_NODES_CNT * maxNoopSrvcMaxPerNodeCnt)
.setMaxPerNodeCount(2);

// Deploying NoopService - should be completed successfully.
client.services().deploy(noopSrvcCfg);

// Check that the expected number of instances has been deployed.
assertEquals(
DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER,
2 * SERVER_NODES_CNT,
totalInstancesCount(client, NOOP_SERVICE_NAME)
);

// Deploy InitThrowingService that throws an exception in init - should not be deployed.
assertThrowsWithCause(
() -> client.services().deploy(new ServiceConfiguration()
.setName(INIT_THROWING_SERVICE_NAME)
.setService(new InitThrowingService())
.setTotalCount(10)
.setMaxPerNodeCount(2)),
ServiceDeploymentException.class
);

assertTrue(
FAILED_SERVICE_SHOULD_NOT_BE_PRESENT_IN_THE_CLUSTER,
waitForCondition(() -> noDescriptorInClusterForService(INIT_THROWING_SERVICE_NAME), TIMEOUT)
);

assertEquals(
DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER,
2 * SERVER_NODES_CNT,
totalInstancesCount(client, NOOP_SERVICE_NAME)
);

client.services().cancel(NOOP_SERVICE_NAME);

// Deploy some additional NoopService instances.
noopSrvcCfg.setMaxPerNodeCount(maxNoopSrvcMaxPerNodeCnt);

client.services().deploy(noopSrvcCfg);

assertEquals(
DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER,
SERVER_NODES_CNT * maxNoopSrvcMaxPerNodeCnt,
totalInstancesCount(client, NOOP_SERVICE_NAME)
);
}

/**
* Tests that service descriptors are clear after an attempt of deploying a service which throws an exception
* during initialization. Static configuration for services is used.
*
* @throws Exception If failed.
*/
@Test
public void testFailedServiceDescriptorsStaticConfiguration() throws Exception {
final int noopSrvcTotalCnt = 20;

startGrids(SERVER_NODES_CNT - 1);

for (int i = 0; i < CLIENT_NODES_CNT; i++)
startClientGrid(SERVER_NODES_CNT + i);

IgniteEx ign = startGrid(
getConfiguration().setServiceConfiguration(
new ServiceConfiguration()
.setName(NOOP_SERVICE_NAME)
.setService(new NoopService())
.setTotalCount(noopSrvcTotalCnt),
new ServiceConfiguration()
.setName(INIT_THROWING_SERVICE_NAME)
.setService(new InitThrowingService())
.setTotalCount(20)
)
);

assertTrue(
FAILED_SERVICE_SHOULD_NOT_BE_PRESENT_IN_THE_CLUSTER,
waitForCondition(() -> noDescriptorInClusterForService(INIT_THROWING_SERVICE_NAME), TIMEOUT)
);

assertEquals(
DEPLOYED_SERVICE_MUST_BE_PRESENTED_IN_CLUSTER,
noopSrvcTotalCnt,
totalInstancesCount(ign, NOOP_SERVICE_NAME)
);
}

/**
* @param srvcName Service name.
*/
private static boolean noDescriptorInClusterForService(String srvcName) {
return Ignition.allGrids().stream().allMatch(
node -> node.services().serviceDescriptors().stream().noneMatch(filterByName(srvcName)) &&
getServicesMap(node, REGISTERED_SERVICES_BY_NAME).values().stream().noneMatch(filterByName(srvcName)) &&
getServicesMap(node, DEPLOYED_SERVICES).values().stream().noneMatch(filterByName(srvcName)) &&
getServicesMap(node, DEPLOYED_SERVICES_BY_NAME).values().stream().noneMatch(filterByName(srvcName))
);
}

/**
* Retrieves the total instances count of the service.
*
* @param igniteEx Ignite instance.
* @param srvcName Service name.
* @return Total instances count of the service named {@code srvcName} from the given {@code igniteEx} instance.
*/
private static int totalInstancesCount(IgniteEx igniteEx, String srvcName) {
int registeredCnt = totalInstancesCount(igniteEx.services().serviceDescriptors(), srvcName);

assertEquals(registeredCnt, totalInstancesCount(getServicesMap(igniteEx, REGISTERED_SERVICES_BY_NAME).values(), srvcName));
assertEquals(registeredCnt, totalInstancesCount(getServicesMap(igniteEx, DEPLOYED_SERVICES).values(), srvcName));
assertEquals(registeredCnt, totalInstancesCount(getServicesMap(igniteEx, DEPLOYED_SERVICES_BY_NAME).values(), srvcName));

return registeredCnt;
}

/**
* @param descs Descriptors.
* @param srvcName Service name.
* @return Total instances count of the service named {@code srvcName} from the given service descriptors {@code descs}.
*/
private static <T> int totalInstancesCount(Collection<? extends ServiceDescriptor> descs, String srvcName) {
return descs.stream()
.filter(filterByName(srvcName))
.flatMap(desc -> desc.topologySnapshot().values().stream())
.mapToInt(Integer::intValue).sum();
}

/**
* Filters descriptors of services with given name.
*
* @param srvcName Service name.
*/
private static @NotNull Predicate<ServiceDescriptor> filterByName(String srvcName) {
return desc -> desc.name().equals(srvcName);
}

/**
* @param node Node.
* @param mapName Map name.
*/
private static <T> Map<T, ServiceInfo> getServicesMap(Ignite node, String mapName) {
return getFieldValue(
((GridKernalContext)getFieldValue(node.services(), IgniteServicesImpl.class, "ctx")).service(),
IgniteServiceProcessor.class,
mapName
);
}

/**
* Service that throws an exception in init.
*/
private static class InitThrowingService implements Service {
/** {@inheritDoc} */
@Override public void init() throws Exception {
throw new Exception("Service init exception");
}
}

/**
* NoopService for testing.
*/
private static class NoopService implements Service {
/** {@inheritDoc} */
@Override public void init() throws Exception {
// No-op.
}
}
}
Loading