[ 
https://issues.apache.org/jira/browse/IGNITE-28874?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18095008#comment-18095008
 ] 

Mikhail Petrov commented on IGNITE-28874:
-----------------------------------------

[~kukushal] Could you please confirm that the issue you described is not 
reproducible on the master branch?

I ran the attached example locally on the master branch and it completed 
successfully.

The goal of the resolved IGNITE-28369 ticket was to fix the very similar (or 
exactly the same) problem.

> Ignite service is never redeployed when its node fails
> ------------------------------------------------------
>
>                 Key: IGNITE-28874
>                 URL: https://issues.apache.org/jira/browse/IGNITE-28874
>             Project: Ignite
>          Issue Type: Bug
>    Affects Versions: 2.18
>            Reporter: Alexey Kukushkin
>            Priority: Major
>
> It is expected that an Ignite service is always redeployed on another 
> available node after its node fails. However, it does not happen if the next 
> node that Ignite chooses for the service also fails during the service 
> redeployment. See the reproducer below.
> {code:java}
> package sandbox.ignite;
> 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.NoOpFailureHandler;
> import org.apache.ignite.lang.IgnitePredicate;
> import org.apache.ignite.services.Service;
> import org.apache.ignite.services.ServiceConfiguration;
> import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
> import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
> import org.junit.jupiter.api.RepeatedTest;
> import java.time.Duration;
> import java.util.Collections;
> import java.util.HashSet;
> import java.util.Optional;
> import java.util.Set;
> import static org.junit.jupiter.api.Assertions.assertTrue;
> public class CfAll44439Test {
>     private final static int FAILURE_DETECTION_TIMEOUT = 2_000;
>     /**
>      * EXPECTED: An Ignite service is always redeployed on the next available 
> node after its node fails.
>      * ACTUAL: The service is never redeployed if the next available node 
> chosen for it fails during
>      * the service redeployment.
>      */
>     @RepeatedTest(10)
>     public void serviceIsRedeployedAfterItsNodeFails() throws 
> InterruptedException {
>         // GIVEN an Ignite cluster consisting of 1 server and 3 client nodes 
> with a 2-second failure detection timeout
>         var aliveClients = new HashSet<>(Set.of("client1", "client2", 
> "client3"));
>         try (final var server1 = 
> Ignition.start(getIgniteConfiguration("server1", false))) {
>             for (final var clientName : aliveClients) {
>                 Ignition.start(getIgniteConfiguration(clientName, true));
>             }
>             // AND an Ignite service named "SingletonService" is deployed on 
> one of the client nodes as a Cluster
>             // Singleton. The "SingletonService" is initially deployed 
> without any delays. However, redeploying
>             // the service takes 4 times the failure detection timeout (8 
> seconds).
>             final var service = new SingletonService(Duration.ofMillis(4 * 
> FAILURE_DETECTION_TIMEOUT));
>             final var serviceName = SingletonService.class.getName();
>             final var serviceConfig = new ServiceConfiguration()
>                 .setService(service)
>                 .setName(serviceName)
>                 .setNodeFilter(new AllClientsPredicate())
>                 .setMaxPerNodeCount(0)
>                 .setTotalCount(1);
>             server1.services().deploy(serviceConfig);
>             final var initialServiceNode = 
> getServiceNodeConsistentId(server1, serviceName);
>             assertTrue(
>                 initialServiceNode.isPresent(),
>                 "Initial setup: all clients are alive, the service is on " + 
> initialServiceNode);
>             // AND the node hosting the "SingletonService" stops
>             Ignition.stop(initialServiceNode.orElseThrow(), true);
>             aliveClients.remove(initialServiceNode.orElseThrow());
>             var nextServiceNode = getServiceNodeConsistentId(server1, 
> serviceName);
>             assertTrue(
>                 nextServiceNode.isEmpty(),
>                 "The service is not deployed immediately after its node is 
> killed since redeployment takes " +
>                     "(4 x timeout)");
>             // AND 4 seconds passes
>             Thread.sleep(2 * FAILURE_DETECTION_TIMEOUT);
>             nextServiceNode = getServiceNodeConsistentId(server1, 
> serviceName);
>             assertTrue(
>                 nextServiceNode.isEmpty(),
>                 "The service is still not deployed after (2 x timeout) since 
> redeployment takes (4 x timeout)");
>             // AND the next node Ignite scheduled to deploy the service on 
> stops
>             // NOTE: at this moment the service is still not redeployed, but 
> Ignite has already chosen the next
>             // node for deployment. There is no API to query that node, so we 
> pick one of the two remaining
>             // client nodes at random. This means there is a 50% chance of 
> picking the wrong node, and thus
>             // the test reproduces the problem with 50% probability. The test 
> is repeated to increase that probability.
>             var nextKilledClient = aliveClients.iterator().next();
>             Ignition.stop(nextKilledClient, true);
>             aliveClients.remove(nextKilledClient);
>             nextServiceNode = getServiceNodeConsistentId(server1, 
> serviceName);
>             assertTrue(
>                 nextServiceNode.isEmpty(),
>                 "The service is still not deployed immediately after another 
> node is killed");
>             // WHEN another 12 seconds pass, making the total time 16 
> seconds, which is twice the
>             // service's 8-second redeployment time.
>             Thread.sleep(6 * FAILURE_DETECTION_TIMEOUT);
>             // THEN the service is redeployed on the remaining client node
>             nextServiceNode = getServiceNodeConsistentId(server1, 
> serviceName);
>             final var remainingClient = aliveClients.iterator().next();
>             assertTrue(
>                 nextServiceNode.isPresent() && 
> nextServiceNode.orElseThrow().equals(remainingClient),
>                 "The service must be deployed on the remaining " + 
> remainingClient + " after (8 x timeout) since " +
>                     "the elapsed time significantly exceeds the service's (4 
> x timeout) redeployment time");
>             Ignition.stop(remainingClient, true);
>         }
>     }
>     private static Optional<String> getServiceNodeConsistentId(Ignite ignite, 
> String serviceName) {
>         return ignite.services().serviceDescriptors().stream()
>             .filter(s -> s.name().equals(serviceName))
>             .findFirst()
>             .flatMap(descriptor -> descriptor
>                 .topologySnapshot()
>                 .keySet()
>                 .stream()
>                 .findFirst()
>                 .flatMap(uuid -> 
> Optional.ofNullable(ignite.cluster().node(uuid)))
>                 .map(node -> node.consistentId().toString()));
>     }
>     private static IgniteConfiguration getIgniteConfiguration(final String 
> igniteName, final boolean isClient) {
>         return new IgniteConfiguration()
>             .setClientMode(isClient)
>             .setIgniteInstanceName(igniteName)
>             .setConsistentId(igniteName)
>             .setFailureDetectionTimeout(FAILURE_DETECTION_TIMEOUT)
>             .setClientFailureDetectionTimeout(FAILURE_DETECTION_TIMEOUT)
>             .setFailureHandler(new NoOpFailureHandler())
>             .setDiscoverySpi(
>                 new TcpDiscoverySpi()
>                     .setIpFinder(new 
> TcpDiscoveryVmIpFinder().setAddresses(Collections.singleton("127.0.0.1:49500")))
>                     .setLocalPort(49500)
>             );
>     }
>     private static class SingletonService implements Service {
>         private static boolean initiallyDeployed = false;
>         private final Duration redeployDuration;
>         private SingletonService(final Duration redeployDuration) {
>             this.redeployDuration = redeployDuration;
>         }
>         @Override
>         public void init() {
>             try {
>                 if (initiallyDeployed) {
>                     Thread.sleep(redeployDuration);
>                 } else {
>                     initiallyDeployed = true;
>                 }
>             } catch (InterruptedException e) {
>                 throw new RuntimeException(e);
>             }
>         }
>     }
>     private static class AllClientsPredicate implements 
> IgnitePredicate<ClusterNode> {
>         @Override
>         public boolean apply(final ClusterNode clusterNode) {
>             return clusterNode.isClient();
>         }
>     }
> }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to