PakhomovAlexander commented on code in PR #2359:
URL: https://github.com/apache/ignite-3/pull/2359#discussion_r1279410035


##########
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/ConnectionHeartBeat.java:
##########
@@ -0,0 +1,126 @@
+/*
+ * 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.cli.core.repl;
+
+import jakarta.inject.Singleton;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.Nullable;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
+import org.apache.ignite.internal.cli.logger.CliLoggers;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.thread.NamedThreadFactory;
+import org.apache.ignite.rest.client.api.NodeManagementApi;
+import org.apache.ignite.rest.client.invoker.ApiException;
+
+/**
+ * Connection to node heart beat.
+ */
+@Singleton
+public class ConnectionHeartBeat {
+
+    private static final IgniteLogger log = 
CliLoggers.forClass(ConnectionHeartBeat.class);
+
+    /** CLI check connection period period. */
+    public static long CLI_CHECK_CONNECTION_PERIOD_SECONDS = 5;
+
+    /** Scheduled executor for connection heartbeat. */
+    @Nullable
+    private ScheduledExecutorService scheduledConnectionHeartbeatExecutor;
+
+    private final ApiClientFactory clientFactory;
+
+    private final List<? extends AsyncConnectionEventListener> listeners;
+
+    private final AtomicBoolean connected = new AtomicBoolean(false);
+
+    public ConnectionHeartBeat(ApiClientFactory clientFactory, List<? extends 
AsyncConnectionEventListener> listeners) {
+        this.clientFactory = clientFactory;
+        this.listeners = listeners;
+    }
+
+    /**
+     * Starts connection heartbeat. By default connection will be checked 
every 5 sec.
+     *
+     * @param sessionInfo session info with node url
+     */
+    public void start(SessionInfo sessionInfo) {
+        connectionEstablished();
+
+        if (scheduledConnectionHeartbeatExecutor == null) {
+            scheduledConnectionHeartbeatExecutor =
+                    Executors.newScheduledThreadPool(1, new 
NamedThreadFactory("cli-check-connection-thread", log));
+
+            //Start connection heart beat
+            scheduledConnectionHeartbeatExecutor.scheduleAtFixedRate(
+                    () -> pingConnection(sessionInfo.nodeUrl()),
+                    0,
+                    CLI_CHECK_CONNECTION_PERIOD_SECONDS,
+                    TimeUnit.SECONDS
+            );
+        }
+    }
+
+    /**
+     * Stops connection heartbeat.
+     */
+    public void stop() {

Review Comment:
   Can you make start/stop methods thread safe? I think synchronized will be ok.



##########
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItConnectionHeartbeatTest.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * 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.cli.commands;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.verify;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.internal.cli.core.repl.ConnectionHeartBeat;
+import org.apache.ignite.internal.cli.core.repl.Session;
+import org.apache.ignite.internal.cli.core.repl.prompt.ReplPromptProvider;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ItConnectionHeartbeatTest extends 
CliCommandTestInitializedIntegrationBase {
+
+    @Inject
+    Session session;
+
+    @Spy
+    @Inject
+    private ReplPromptProvider replPromptProvider;
+
+    @BeforeEach
+    void setUp() {
+        //Set connection check timeout to 1 sec to make test fast
+        ConnectionHeartBeat.CLI_CHECK_CONNECTION_PERIOD_SECONDS = 1L;
+    }
+
+    @AfterEach
+    void teardown() {
+        // Start all nodes
+        allNodeNames().forEach(this::startNode);
+    }
+
+    @Override
+    protected Class<?> getCommandClass() {
+        return TopLevelCliReplCommand.class;
+    }
+
+    @Test
+    @DisplayName("Should invoke onConnection() on connection established")
+    void connectionEstablished() {
+        // Given null session info before connect
+        assertNull(session.info());
+
+        // When connect without parameters
+        execute("connect");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        verify(replPromptProvider, 
after(ConnectionHeartBeat.CLI_CHECK_CONNECTION_PERIOD_SECONDS * 
2L).times(1)).onConnection();
+        verify(replPromptProvider, 
after(ConnectionHeartBeat.CLI_CHECK_CONNECTION_PERIOD_SECONDS * 
2L).never()).onConnectionLost();
+    }
+
+    @Test
+    @DisplayName("Should invoke onConnectionLost()")
+    void onConnectionLost() {
+        // Given connected cli
+        execute("connect");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // When stop all nodes
+        allNodeNames().forEach(IgnitionManager::stop);
+
+        verify(replPromptProvider, 
after(ConnectionHeartBeat.CLI_CHECK_CONNECTION_PERIOD_SECONDS * 
2L).times(1)).onConnectionLost();

Review Comment:
   It would be safer to use the `Awaitility` library here and above. Something 
can go wrong on CI and we'll get one more flaky test.



##########
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItConnectionHeartbeatTest.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * 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.cli.commands;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.verify;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.internal.cli.core.repl.ConnectionHeartBeat;
+import org.apache.ignite.internal.cli.core.repl.Session;
+import org.apache.ignite.internal.cli.core.repl.prompt.ReplPromptProvider;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ItConnectionHeartbeatTest extends 
CliCommandTestInitializedIntegrationBase {
+
+    @Inject
+    Session session;
+
+    @Spy
+    @Inject
+    private ReplPromptProvider replPromptProvider;
+
+    @BeforeEach
+    void setUp() {
+        //Set connection check timeout to 1 sec to make test fast
+        ConnectionHeartBeat.CLI_CHECK_CONNECTION_PERIOD_SECONDS = 1L;
+    }
+
+    @AfterEach
+    void teardown() {
+        // Start all nodes
+        allNodeNames().forEach(this::startNode);

Review Comment:
   I don't think you want to start all nodes on the test tear-down.



##########
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItConnectionHeartbeatTest.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * 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.cli.commands;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.verify;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.IgnitionManager;
+import org.apache.ignite.internal.cli.core.repl.ConnectionHeartBeat;
+import org.apache.ignite.internal.cli.core.repl.Session;
+import org.apache.ignite.internal.cli.core.repl.prompt.ReplPromptProvider;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ItConnectionHeartbeatTest extends 
CliCommandTestInitializedIntegrationBase {
+
+    @Inject
+    Session session;
+
+    @Spy
+    @Inject
+    private ReplPromptProvider replPromptProvider;
+
+    @BeforeEach
+    void setUp() {
+        //Set connection check timeout to 1 sec to make test fast
+        ConnectionHeartBeat.CLI_CHECK_CONNECTION_PERIOD_SECONDS = 1L;

Review Comment:
   We tend not to use global static mutable variables (only in extreme cases). 
I would ask you to put the configuration value into `ConnectionHeartBeat` via a 
constructor. In test, you can create the instance directly or using Micronaut's 
configuration property.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to