Merge "Create model in clamp runtime for the replica table"
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationComposition.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationComposition.java
index 0cf1f99..eb5b6dc 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationComposition.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationComposition.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
+ * Copyright (C) 2021-2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -52,6 +52,10 @@
     @NonNull
     private LockState lockState = LockState.NONE;
 
+    private String lastMsg;
+
+    private Integer phase;
+
     private Map<UUID, AutomationCompositionElement> elements;
 
     private StateChangeResult stateChangeResult;
@@ -69,6 +73,8 @@
         this.restarting = otherAutomationComposition.restarting;
         this.deployState = otherAutomationComposition.deployState;
         this.lockState = otherAutomationComposition.lockState;
+        this.lastMsg = otherAutomationComposition.lastMsg;
+        this.phase = otherAutomationComposition.phase;
         this.elements = PfUtils.mapMap(otherAutomationComposition.elements, AutomationCompositionElement::new);
         this.stateChangeResult = otherAutomationComposition.stateChangeResult;
     }
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationComposition.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationComposition.java
index 5e27fde..0bf6a9e 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationComposition.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationComposition.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
+ * Copyright (C) 2021-2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.OneToMany;
 import jakarta.persistence.Table;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -44,6 +45,7 @@
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
 import org.onap.policy.common.parameters.annotations.NotNull;
 import org.onap.policy.common.parameters.annotations.Valid;
 import org.onap.policy.models.base.PfAuthorative;
@@ -98,6 +100,13 @@
     private StateChangeResult stateChangeResult;
 
     @Column
+    @NotNull
+    private Timestamp lastMsg;
+
+    @Column
+    private Integer phase;
+
+    @Column
     private String description;
 
     @NotNull
@@ -149,6 +158,8 @@
         this.restarting = copyConcept.restarting;
         this.deployState = copyConcept.deployState;
         this.lockState = copyConcept.lockState;
+        this.lastMsg = copyConcept.lastMsg;
+        this.phase = copyConcept.phase;
         this.description = copyConcept.description;
         this.stateChangeResult = copyConcept.stateChangeResult;
         this.elements = PfUtils.mapList(copyConcept.elements, JpaAutomationCompositionElement::new);
@@ -177,6 +188,8 @@
         automationComposition.setRestarting(restarting);
         automationComposition.setDeployState(deployState);
         automationComposition.setLockState(lockState);
+        automationComposition.setLastMsg(lastMsg.toString());
+        automationComposition.setPhase(phase);
         automationComposition.setDescription(description);
         automationComposition.setStateChangeResult(stateChangeResult);
         automationComposition.setElements(new LinkedHashMap<>(this.elements.size()));
@@ -199,6 +212,8 @@
         this.restarting = automationComposition.getRestarting();
         this.deployState = automationComposition.getDeployState();
         this.lockState = automationComposition.getLockState();
+        this.lastMsg = TimestampHelper.toTimestamp(automationComposition.getLastMsg());
+        this.phase = automationComposition.getPhase();
         this.description = automationComposition.getDescription();
         this.stateChangeResult = automationComposition.getStateChangeResult();
         this.elements = new ArrayList<>(automationComposition.getElements().size());
@@ -229,6 +244,16 @@
             return result;
         }
 
+        result = lastMsg.compareTo(other.lastMsg);
+        if (result != 0) {
+            return result;
+        }
+
+        result = ObjectUtils.compare(phase, other.phase);
+        if (result != 0) {
+            return result;
+        }
+
         result = ObjectUtils.compare(version, other.version);
         if (result != 0) {
             return result;
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java b/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java
index 0293bd3..5f523ad 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/utils/AcmUtils.java
@@ -29,7 +29,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Queue;
 import java.util.UUID;
 import java.util.function.Function;
 import java.util.function.UnaryOperator;
@@ -71,7 +70,7 @@
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public final class AcmUtils {
     public static final String ENTRY = "entry ";
-    private static StringToMapConverter MAP_CONVERTER = new StringToMapConverter();
+    private static final StringToMapConverter MAP_CONVERTER = new StringToMapConverter();
 
     /**
      * Get the Policy information in the service template for the deploy message to participants.
@@ -379,6 +378,7 @@
             final DeployState deployState, final LockState lockState) {
         automationComposition.setDeployState(deployState);
         automationComposition.setLockState(lockState);
+        automationComposition.setLastMsg(TimestampHelper.now());
 
         if (MapUtils.isEmpty(automationComposition.getElements())) {
             return;
diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionTest.java
index 66554e7..b56e778 100644
--- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionTest.java
+++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionTest.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
+ * Copyright (C) 2021-2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,8 @@
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.UUID;
@@ -35,6 +37,7 @@
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.utils.TimestampHelper;
 import org.onap.policy.models.base.PfConceptKey;
 
 /**
@@ -93,9 +96,9 @@
 
     @Test
     void testJpaAutomationComposition() {
-        var jpaAutomationComposition = createJpaAutomationCompositionInstance();
-
         var automationComposition = createAutomationCompositionInstance();
+        var jpaAutomationComposition = new JpaAutomationComposition(automationComposition);
+
         assertEquals(automationComposition, jpaAutomationComposition.toAuthorative());
 
         var target = UUID.randomUUID();
@@ -125,7 +128,7 @@
 
     @Test
     void testJpaAutomationCompositionValidation() {
-        var testJpaAutomationComposition = createJpaAutomationCompositionInstance();
+        var testJpaAutomationComposition = new JpaAutomationComposition(createAutomationCompositionInstance());
 
         assertThatThrownBy(() -> testJpaAutomationComposition.validate(null))
                 .hasMessageMatching("fieldName is marked .*ull but is null");
@@ -135,7 +138,7 @@
 
     @Test
     void testJpaAutomationCompositionCompareTo() {
-        var jpaAutomationComposition = createJpaAutomationCompositionInstance();
+        var jpaAutomationComposition = new JpaAutomationComposition(createAutomationCompositionInstance());
 
         var otherJpaAutomationComposition = new JpaAutomationComposition(jpaAutomationComposition);
         assertEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
@@ -168,6 +171,16 @@
         jpaAutomationComposition.setVersion("0.0.1");
         assertEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
 
+        jpaAutomationComposition.setLastMsg(Timestamp.from(Instant.EPOCH));
+        assertNotEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
+        jpaAutomationComposition.setLastMsg(otherJpaAutomationComposition.getLastMsg());
+        assertEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
+
+        jpaAutomationComposition.setPhase(0);
+        assertNotEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
+        jpaAutomationComposition.setPhase(null);
+        assertEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
+
         jpaAutomationComposition.setDeployState(DeployState.DEPLOYED);
         assertNotEquals(0, jpaAutomationComposition.compareTo(otherJpaAutomationComposition));
         jpaAutomationComposition.setDeployState(DeployState.UNDEPLOYED);
@@ -225,19 +238,12 @@
         assertEquals(ac2, ac0);
     }
 
-    private JpaAutomationComposition createJpaAutomationCompositionInstance() {
-        var testAutomationComposition = createAutomationCompositionInstance();
-        var testJpaAutomationComposition = new JpaAutomationComposition();
-        testJpaAutomationComposition.fromAuthorative(testAutomationComposition);
-
-        return testJpaAutomationComposition;
-    }
-
     private AutomationComposition createAutomationCompositionInstance() {
         var testAutomationComposition = new AutomationComposition();
         testAutomationComposition.setName("automation-composition");
         testAutomationComposition.setInstanceId(UUID.fromString(INSTANCE_ID));
         testAutomationComposition.setVersion("0.0.1");
+        testAutomationComposition.setLastMsg(TimestampHelper.now());
         testAutomationComposition.setCompositionId(UUID.fromString(COMPOSITION_ID));
         testAutomationComposition.setElements(new LinkedHashMap<>());
 
diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
index 463e958..8e7e50d 100644
--- a/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
+++ b/models/src/test/java/org/onap/policy/clamp/models/acm/persistence/provider/AutomationCompositionProviderTest.java
@@ -85,6 +85,7 @@
 
         var createdAutomationComposition = automationCompositionProvider.createAutomationComposition(inputAc);
         inputAc.setInstanceId(createdAutomationComposition.getInstanceId());
+        inputAc.setLastMsg(createdAutomationComposition.getLastMsg());
         assertEquals(inputAc, createdAutomationComposition);
     }
 
diff --git a/models/src/test/resources/providers/TestAutomationCompositions.json b/models/src/test/resources/providers/TestAutomationCompositions.json
index 24f5a48..c75337b 100644
--- a/models/src/test/resources/providers/TestAutomationCompositions.json
+++ b/models/src/test/resources/providers/TestAutomationCompositions.json
@@ -5,6 +5,7 @@
             "instanceId": "809c62b3-8918-41b9-a748-e21eb79c6c89",
             "deployState": "UNDEPLOYED",
             "lockState": "NONE",
+            "lastMsg": "2024-05-22 10:04:37.6020187",
             "elements": {
                 "709c62b3-8918-41b9-a747-e21eb79c6c20": {
                     "id": "709c62b3-8918-41b9-a747-e21eb79c6c20",
@@ -56,6 +57,7 @@
             "instanceId": "809c62b3-8918-41b9-a748-e21eb79c6c90",
             "deployState": "UNDEPLOYED",
             "lockState": "NONE",
+            "lastMsg": "2024-05-22 10:04:37.6020187",
             "elements": {
                 "709c62b3-8918-41b9-a747-e21eb79c6c24": {
                     "id": "709c62b3-8918-41b9-a747-e21eb79c6c24",
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
index d6fa5d8..802c660 100644
--- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
@@ -86,8 +86,9 @@
             AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYING, LockState.NONE);
         }
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
-        automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
+        automationComposition.setPhase(startPhase);
+        automationCompositionProvider.updateAutomationComposition(automationComposition);
         executor.execute(
             () -> automationCompositionDeployPublisher.send(automationComposition, acDefinition.getServiceTemplate(),
                 startPhase, true));
@@ -112,8 +113,9 @@
         }
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         automationComposition.setCompositionTargetId(null);
-        automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
+        automationComposition.setPhase(startPhase);
+        automationCompositionProvider.updateAutomationComposition(automationComposition);
         executor.execute(
             () -> automationCompositionStateChangePublisher.send(automationComposition, startPhase, true));
     }
@@ -136,8 +138,9 @@
             AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.UNLOCKING);
         }
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
-        automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
+        automationComposition.setPhase(startPhase);
+        automationCompositionProvider.updateAutomationComposition(automationComposition);
         executor.execute(
             () -> automationCompositionStateChangePublisher.send(automationComposition, startPhase, true));
     }
@@ -160,8 +163,9 @@
             AcmUtils.setCascadedState(automationComposition, DeployState.DEPLOYED, LockState.LOCKING);
         }
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
-        automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
+        automationComposition.setPhase(startPhase);
+        automationCompositionProvider.updateAutomationComposition(automationComposition);
         executor.execute(
             () -> automationCompositionStateChangePublisher.send(automationComposition, startPhase, true));
     }
@@ -187,8 +191,9 @@
     public void delete(AutomationComposition automationComposition, AutomationCompositionDefinition acDefinition) {
         AcmUtils.setCascadedState(automationComposition, DeployState.DELETING, LockState.NONE);
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
-        automationCompositionProvider.updateAutomationComposition(automationComposition);
         var startPhase = ParticipantUtils.getFirstStartPhase(automationComposition, acDefinition.getServiceTemplate());
+        automationComposition.setPhase(startPhase);
+        automationCompositionProvider.updateAutomationComposition(automationComposition);
         executor.execute(
             () -> automationCompositionStateChangePublisher.send(automationComposition, startPhase, true));
     }
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java
index 96e75df..06d4646 100644
--- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScanner.java
@@ -23,9 +23,7 @@
 package org.onap.policy.clamp.acm.runtime.supervision;
 
 import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
-import java.util.stream.Collectors;
 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionDeployPublisher;
 import org.onap.policy.clamp.acm.runtime.supervision.comm.AutomationCompositionStateChangePublisher;
@@ -51,9 +49,6 @@
 public class SupervisionScanner {
     private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class);
 
-    private final TimeoutHandler<UUID> acTimeout = new TimeoutHandler<>();
-    private final Map<UUID, Integer> phaseMap = new HashMap<>();
-
     private final long maxStatusWaitMs;
 
     private final AutomationCompositionProvider automationCompositionProvider;
@@ -79,8 +74,6 @@
         this.acDefinitionProvider = acDefinitionProvider;
         this.automationCompositionStateChangePublisher = automationCompositionStateChangePublisher;
         this.automationCompositionDeployPublisher = automationCompositionDeployPublisher;
-
-        acTimeout.setMaxWaitMs(acRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs());
         this.maxStatusWaitMs = acRuntimeParameterGroup.getParticipantParameters().getMaxStatusWaitMs();
     }
 
@@ -105,9 +98,6 @@
             }
             scanAutomationComposition(automationComposition, acDefinition.getServiceTemplate());
         }
-        var set = acList.stream().map(AutomationComposition::getInstanceId).collect(Collectors.toSet());
-        acTimeout.removeIfNotPresent(set);
-
         LOGGER.debug("Automation composition scan complete . . .");
     }
 
@@ -143,17 +133,9 @@
             LOGGER.debug("automation composition {} scanned, OK", automationComposition.getInstanceId());
 
             // Clear Timeout on automation composition
-            removeTimeout(automationComposition);
             return;
         }
 
-        if (acTimeout.isTimeout(automationComposition.getInstanceId())
-                && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
-            // retry by the user
-            LOGGER.debug("clearing Timeout for the ac instance");
-            clearTimeout(automationComposition, true);
-        }
-
         var completed = true;
         var minSpNotCompleted = 1000; // min startPhase not completed
         var maxSpNotCompleted = 0; // max startPhase not completed
@@ -173,18 +155,18 @@
         }
 
         if (completed) {
-            LOGGER.debug("automation composition scan: transition state {} {} ", automationComposition.getDeployState(),
-                    automationComposition.getLockState());
+            LOGGER.debug("automation composition scan: transition state {} {} completed",
+                    automationComposition.getDeployState(), automationComposition.getLockState());
 
             complete(automationComposition);
         } else {
-            LOGGER.debug("automation composition scan: transition from state {} to {} not completed",
+            LOGGER.debug("automation composition scan: transition state {} {} not completed",
                     automationComposition.getDeployState(), automationComposition.getLockState());
 
             if (DeployState.UPDATING.equals(automationComposition.getDeployState())
                     || DeployState.MIGRATING.equals(automationComposition.getDeployState())) {
                 // UPDATING do not need phases
-                handleTimeout(automationComposition);
+                handleTimeoutUpdate(automationComposition);
                 return;
             }
 
@@ -192,14 +174,11 @@
                     AcmUtils.isForward(automationComposition.getDeployState(), automationComposition.getLockState());
 
             var nextSpNotCompleted = isForward ? minSpNotCompleted : maxSpNotCompleted;
-            var firstStartPhase = isForward ? defaultMin : defaultMax;
 
-            if (nextSpNotCompleted != phaseMap.getOrDefault(automationComposition.getInstanceId(), firstStartPhase)) {
-                phaseMap.put(automationComposition.getInstanceId(), nextSpNotCompleted);
-                sendAutomationCompositionMsg(automationComposition, serviceTemplate, nextSpNotCompleted,
-                        firstStartPhase == nextSpNotCompleted);
+            if (nextSpNotCompleted != automationComposition.getPhase()) {
+                sendAutomationCompositionMsg(automationComposition, serviceTemplate, nextSpNotCompleted, false);
             } else {
-                handleTimeout(automationComposition);
+                handleTimeoutWithPhase(automationComposition, serviceTemplate);
             }
         }
     }
@@ -213,6 +192,7 @@
         }
         automationComposition.setDeployState(AcmUtils.deployCompleted(deployState));
         automationComposition.setLockState(AcmUtils.lockCompleted(deployState, automationComposition.getLockState()));
+        automationComposition.setPhase(null);
         if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
             automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
         }
@@ -221,21 +201,6 @@
         } else {
             automationCompositionProvider.updateAutomationComposition(automationComposition);
         }
-
-        // Clear timeout on automation composition
-        removeTimeout(automationComposition);
-    }
-
-    private void clearTimeout(AutomationComposition automationComposition, boolean cleanPhase) {
-        acTimeout.clear(automationComposition.getInstanceId());
-        if (cleanPhase) {
-            phaseMap.remove(automationComposition.getInstanceId());
-        }
-    }
-
-    private void removeTimeout(AutomationComposition automationComposition) {
-        acTimeout.remove(automationComposition.getInstanceId());
-        phaseMap.remove(automationComposition.getInstanceId());
     }
 
     private void handleTimeout(AutomationCompositionDefinition acDefinition) {
@@ -253,23 +218,60 @@
         }
     }
 
-    private void handleTimeout(AutomationComposition automationComposition) {
-        var instanceId = automationComposition.getInstanceId();
-        if (acTimeout.isTimeout(instanceId)) {
+    private void handleTimeoutUpdate(AutomationComposition automationComposition) {
+        if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
             LOGGER.debug("The ac instance is in timeout {}", automationComposition.getInstanceId());
             return;
         }
+        var now = TimestampHelper.nowEpochMilli();
+        var lastMsg = TimestampHelper.toEpochMilli(automationComposition.getLastMsg());
+        for (var element : automationComposition.getElements().values()) {
+            if (!AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState())) {
+                continue;
+            }
+            if ((now - lastMsg) > maxStatusWaitMs) {
+                LOGGER.debug("Report timeout for the ac instance {}", automationComposition.getInstanceId());
+                automationComposition.setStateChangeResult(StateChangeResult.TIMEOUT);
+                automationCompositionProvider.updateAutomationComposition(automationComposition);
+                break;
+            }
+        }
+    }
 
-        if (acTimeout.getDuration(instanceId) > acTimeout.getMaxWaitMs()) {
-            LOGGER.debug("Report timeout for the ac instance {}", automationComposition.getInstanceId());
-            acTimeout.setTimeout(instanceId);
-            automationComposition.setStateChangeResult(StateChangeResult.TIMEOUT);
-            automationCompositionProvider.updateAutomationComposition(automationComposition);
+    private void handleTimeoutWithPhase(AutomationComposition automationComposition,
+            ToscaServiceTemplate serviceTemplate) {
+        if (StateChangeResult.TIMEOUT.equals(automationComposition.getStateChangeResult())) {
+            LOGGER.debug("The ac instance is in timeout {}", automationComposition.getInstanceId());
+            return;
+        }
+        int currentPhase = automationComposition.getPhase();
+        var now = TimestampHelper.nowEpochMilli();
+        var lastMsg = TimestampHelper.toEpochMilli(automationComposition.getLastMsg());
+        for (var element : automationComposition.getElements().values()) {
+            if (!AcmUtils.isInTransitionalState(element.getDeployState(), element.getLockState())) {
+                continue;
+            }
+            var toscaNodeTemplate = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+                    .get(element.getDefinition().getName());
+            int startPhase = ParticipantUtils.findStartPhase(toscaNodeTemplate.getProperties());
+            if (currentPhase != startPhase) {
+                continue;
+            }
+            if ((now - lastMsg) > maxStatusWaitMs) {
+                LOGGER.debug("Report timeout for the ac instance {}", automationComposition.getInstanceId());
+                automationComposition.setStateChangeResult(StateChangeResult.TIMEOUT);
+                automationCompositionProvider.updateAutomationComposition(automationComposition);
+                break;
+            }
         }
     }
 
     private void sendAutomationCompositionMsg(AutomationComposition automationComposition,
             ToscaServiceTemplate serviceTemplate, int startPhase, boolean firstStartPhase) {
+        automationComposition.setLastMsg(TimestampHelper.now());
+        automationComposition.setPhase(startPhase);
+        automationCompositionProvider.updateAutomationComposition(automationComposition);
+
         if (DeployState.DEPLOYING.equals(automationComposition.getDeployState())) {
             LOGGER.debug("retry message AutomationCompositionUpdate");
             automationCompositionDeployPublisher.send(automationComposition, serviceTemplate, startPhase,
@@ -278,7 +280,5 @@
             LOGGER.debug("retry message AutomationCompositionStateChange");
             automationCompositionStateChangePublisher.send(automationComposition, startPhase, firstStartPhase);
         }
-        // Clear timeout on automation composition
-        clearTimeout(automationComposition, false);
     }
 }
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/TimeoutHandler.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/TimeoutHandler.java
deleted file mode 100644
index 3b34252..0000000
--- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/TimeoutHandler.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2023-2024 Nordix Foundation.
- * ================================================================================
- * Licensed 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.clamp.acm.runtime.supervision;
-
-import java.time.Instant;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import lombok.Getter;
-import lombok.Setter;
-
-public class TimeoutHandler<K> {
-    @Getter
-    @Setter
-    private long maxWaitMs;
-
-    private final Set<K> mapTimeout = new HashSet<>();
-    private final Map<K, Long> mapTimer = new HashMap<>();
-
-    public long getDuration(K id) {
-        mapTimer.putIfAbsent(id, getEpochMilli());
-        return getEpochMilli() - mapTimer.get(id);
-    }
-
-    /**
-     * Reset timer and timeout by id.
-     *
-     * @param id the id
-     */
-    public void clear(K id) {
-        mapTimeout.remove(id);
-        mapTimer.put(id, getEpochMilli());
-    }
-
-    /**
-     * Remove timer and timeout by id.
-     *
-     * @param id the id
-     */
-    public void remove(K id) {
-        mapTimeout.remove(id);
-        mapTimer.remove(id);
-    }
-
-    /**
-     * Remove elements that are not present in set.
-     *
-     * @param set the elements that should be present
-     */
-    public void removeIfNotPresent(final Set<K> set) {
-        var res = mapTimeout.stream().filter(el -> !set.contains(el)).toList();
-        if (!res.isEmpty()) {
-            res.forEach(this::remove);
-        }
-    }
-
-    public void setTimeout(K id) {
-        mapTimeout.add(id);
-    }
-
-    public boolean isTimeout(K id) {
-        return mapTimeout.contains(id);
-    }
-
-    protected long getEpochMilli() {
-        return Instant.now().toEpochMilli();
-    }
-}
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java
index cd1a385..bcfdea1 100644
--- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/instantiation/rest/InstantiationControllerTest.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation.
+ *  Copyright (C) 2021-2024 Nordix Foundation.
  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -155,6 +155,7 @@
 
         var automationCompositionFromDb =
                 instantiationProvider.getAutomationComposition(compositionId, instResponse.getInstanceId());
+        automationCompositionFromRsc.setLastMsg(automationCompositionFromDb.getLastMsg());
 
         assertNotNull(automationCompositionFromDb);
         assertEquals(automationCompositionFromRsc, automationCompositionFromDb);
@@ -223,7 +224,9 @@
         var automationCompositionsQuery = rawresp.readEntity(AutomationCompositions.class);
         assertNotNull(automationCompositionsQuery);
         assertThat(automationCompositionsQuery.getAutomationCompositionList()).hasSize(1);
-        assertEquals(automationComposition, automationCompositionsQuery.getAutomationCompositionList().get(0));
+        var automationCompositionRc = automationCompositionsQuery.getAutomationCompositionList().get(0);
+        automationComposition.setLastMsg(automationCompositionRc.getLastMsg());
+        assertEquals(automationComposition, automationCompositionRc);
     }
 
     @Test
@@ -241,6 +244,7 @@
         assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
         var automationCompositionGet = rawresp.readEntity(AutomationComposition.class);
         assertNotNull(automationCompositionGet);
+        automationComposition.setLastMsg(automationCompositionGet.getLastMsg());
         assertEquals(automationComposition, automationCompositionGet);
     }
 
@@ -272,7 +276,9 @@
 
         assertNotNull(automationCompositionsFromDb);
         assertThat(automationCompositionsFromDb.getAutomationCompositionList()).hasSize(1);
-        assertEquals(automationComposition, automationCompositionsFromDb.getAutomationCompositionList().get(0));
+        var acFromDb = automationCompositionsFromDb.getAutomationCompositionList().get(0);
+        automationComposition.setLastMsg(acFromDb.getLastMsg());
+        assertEquals(automationComposition, acFromDb);
     }
 
     @Test
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
index 8ed250f..d5163be 100644
--- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionScannerTest.java
@@ -243,10 +243,15 @@
         var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
         automationComposition.setDeployState(DeployState.DEPLOYING);
         automationComposition.setLockState(LockState.NONE);
+        automationComposition.setPhase(0);
         automationComposition.setCompositionId(compositionId);
-        for (Map.Entry<UUID, AutomationCompositionElement> entry : automationComposition.getElements().entrySet()) {
+        for (var entry : automationComposition.getElements().entrySet()) {
             entry.getValue().setDeployState(DeployState.DEPLOYING);
         }
+        // the first element is already completed
+        automationComposition.getElements().entrySet().iterator().next().getValue()
+                .setDeployState(DeployState.DEPLOYED);
+
         var automationCompositionProvider = mock(AutomationCompositionProvider.class);
         when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
 
@@ -261,14 +266,22 @@
                 acRuntimeParameterGroup);
 
         automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
+        automationComposition.setLastMsg(TimestampHelper.now());
         scannerObj2.run();
         verify(automationCompositionProvider, times(1)).updateAutomationComposition(any(AutomationComposition.class));
         assertEquals(StateChangeResult.TIMEOUT, automationComposition.getStateChangeResult());
 
+        //already in TIMEOUT
+        clearInvocations(automationCompositionProvider);
+        scannerObj2.run();
+        verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
+
+        clearInvocations(automationCompositionProvider);
         for (Map.Entry<UUID, AutomationCompositionElement> entry : automationComposition.getElements().entrySet()) {
             entry.getValue().setDeployState(DeployState.DEPLOYED);
         }
         scannerObj2.run();
+        verify(automationCompositionProvider, times(1)).updateAutomationComposition(any(AutomationComposition.class));
         assertEquals(StateChangeResult.NO_ERROR, automationComposition.getStateChangeResult());
     }
 
@@ -277,6 +290,7 @@
         var automationComposition = InstantiationUtils.getAutomationCompositionFromResource(AC_JSON, "Crud");
         automationComposition.setDeployState(DeployState.DEPLOYING);
         automationComposition.setLockState(LockState.NONE);
+        automationComposition.setPhase(0);
         automationComposition.setCompositionId(compositionId);
         for (var element : automationComposition.getElements().values()) {
             if ("org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement"
@@ -314,10 +328,15 @@
         var compositionTargetId = UUID.randomUUID();
         automationComposition.setCompositionTargetId(compositionTargetId);
         automationComposition.setLockState(LockState.LOCKED);
+        automationComposition.setLastMsg(TimestampHelper.now());
+        automationComposition.setPhase(0);
         for (var element : automationComposition.getElements().values()) {
             element.setDeployState(DeployState.DEPLOYED);
             element.setLockState(LockState.LOCKED);
         }
+        // first element is not migrated yet
+        automationComposition.getElements().entrySet().iterator().next().getValue()
+                .setDeployState(DeployState.MIGRATING);
 
         var automationCompositionProvider = mock(AutomationCompositionProvider.class);
         when(automationCompositionProvider.getAcInstancesInTransition()).thenReturn(List.of(automationComposition));
@@ -331,7 +350,15 @@
                 acRuntimeParameterGroup);
 
         supervisionScanner.run();
+        verify(automationCompositionProvider, times(0)).updateAutomationComposition(any(AutomationComposition.class));
+        assertEquals(DeployState.MIGRATING, automationComposition.getDeployState());
+
+        // first element is migrated
+        automationComposition.getElements().entrySet().iterator().next().getValue()
+                .setDeployState(DeployState.DEPLOYED);
+        supervisionScanner.run();
         verify(automationCompositionProvider, times(1)).updateAutomationComposition(any(AutomationComposition.class));
+
         assertEquals(DeployState.DEPLOYED, automationComposition.getDeployState());
         assertEquals(compositionTargetId, automationComposition.getCompositionId());
     }
@@ -342,6 +369,7 @@
         automationComposition.setDeployState(DeployState.DEPLOYED);
         automationComposition.setLockState(LockState.UNLOCKING);
         automationComposition.setCompositionId(compositionId);
+        automationComposition.setPhase(0);
         for (var element : automationComposition.getElements().values()) {
             if ("org.onap.domain.database.Http_PMSHMicroserviceAutomationCompositionElement"
                     .equals(element.getDefinition().getName())) {
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/TimeoutHandlerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/TimeoutHandlerTest.java
deleted file mode 100644
index 21c5b3d..0000000
--- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/TimeoutHandlerTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- *  Copyright (C) 2023 Nordix Foundation.
- * ================================================================================
- * Licensed 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.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.clamp.acm.runtime.supervision;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.jupiter.api.Test;
-
-class TimeoutHandlerTest {
-
-    private static final int ID = 1;
-
-    @Test
-    void testFault() {
-        var timeoutHandler = new TimeoutHandler<Integer>();
-        timeoutHandler.setTimeout(ID);
-        assertThat(timeoutHandler.isTimeout(ID)).isTrue();
-        timeoutHandler.clear(ID);
-        assertThat(timeoutHandler.isTimeout(ID)).isFalse();
-    }
-
-    @Test
-    void testDuration() {
-        var timeoutHandler = new TimeoutHandler<Integer>() {
-            long epochMilli = 0;
-
-            @Override
-            protected long getEpochMilli() {
-                return epochMilli;
-            }
-        };
-        timeoutHandler.epochMilli = 100;
-        var result = timeoutHandler.getDuration(ID);
-        assertThat(result).isZero();
-
-        timeoutHandler.epochMilli += 100;
-        result = timeoutHandler.getDuration(ID);
-        assertThat(result).isEqualTo(100);
-
-        timeoutHandler.epochMilli += 100;
-        result = timeoutHandler.getDuration(ID);
-        assertThat(result).isEqualTo(200);
-
-        timeoutHandler.epochMilli += 100;
-        timeoutHandler.clear(ID);
-        result = timeoutHandler.getDuration(ID);
-        assertThat(result).isZero();
-    }
-}