Brief introduction
Example of scenario with a deployed instance with two elements:
- user call undeploy command
- first instance element success with UNDEPLOYED state
- second instance element fail with DEPLOYED state
- user call undeploy command again
- first instance element does not know that the element is already UNDEPLOYED
Add support for the participant to understand what was the last state.
Allowed state from the participant perspective
Action | state | stChResult | Description |
Prime | PRIMED | NO_ERROR | Prime is completed |
COMMISSIONED | FAILED | Prime is failed | |
Deprime | COMMISSIONED | NO_ERROR | Deprime is completed |
DEPRIMED | FAILED | Deprime is failed |
Action | deployState | lockState | stChResult | Description |
Deploy | DEPLOYED | NO_ERROR | Deploy is completed | |
UNDEPLOYED | FAILED | Deploy is failed | ||
Undeploy | UNDEPLOYED | NO_ERROR | Undeploy is completed | |
DEPLOYED | FAILED | Undeploy is failed | ||
Lock | LOCKED | NO_ERROR | Lock is completed | |
UNLOCKED | FAILED | Lock is failed | ||
Unlock | UNLOCKED | NO_ERROR | Unlock is completed | |
LOCKED | FAILED | Unlock is failed | ||
Update | DEPLOYED | NO_ERROR | Update is completed | |
DEPLOYED | FAILED | Update is failed | ||
Migrate | DEPLOYED | NO_ERROR | Migrate is completed | |
DEPLOYED | FAILED | Migrate is failed | ||
Delete | DELETED | NO_ERROR | Delete is completed | |
UNDEPLOYED | FAILED | Delete is failed |
Additional comments
It is important to make distinction between the state of the instance/element in the flow, and the state of the application involved. A deployed element means that a participant has completed a deploy action, and should not be confused with a deployed application.
There are scenarios where it make sense when a process is failed, all elements should repeat the action, but in other scenarios it could be different, it depends on the context. Anyway, a participant could implement idempotent operations.
The responsibility to know what it needs to be done is delegated to the participant.
When a participant cannot implement idempotent operations, in order to cover the scenario with failed process with more than one element involved, it has to know what was the previous state.
Solutions
A simple solution from participant side is that it can use "outProperties" to save information related to the deployment process.
"outProperties" is controlled by the participant side, and saved into ACM-r Db by status message (by sendAcElementInfo method). Each participant replica will be synchronized as well.
The examples below are suggestions.
- Below a simple example:
@Override public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { if ("DEPLOYED".equals(instanceElement.outProperties().get("state"))) { // deploy process already done intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Already Deployed"); return; } // deployment process ....................................... ....................................... // end of the deployment process if (isDeploySuccess()) { instanceElement.outProperties().put("state", "DEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed"); } else { instanceElement.outProperties().put("state", "UNDEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!"); } } @Override public void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { if ("DEPLOYED".equals(instanceElement.outProperties().get("state"))) { // undeploy process already done intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Already Undeployed"); return; } // undeployment process ....................................... ....................................... // end of the undeployment process if (isUndeploySuccess()) { instanceElement.outProperties().put("state", "UNDEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed"); } else { instanceElement.outProperties().put("state", "DEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Undeploy failed!"); } }
- Below other suggestion when a process is failed, all elements can repeat the action and do some clean up
@Override public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { var state = instanceElement.outProperties().get("state"); if ("DEPLOYED".equals(state)) { // clean up deployment } else if ("DEPLOYING".equals(state) || "UNDEPLOYING".equals(state)) { // check and clean up } // deployment process instanceElement.outProperties().put("state", "DEPLOYING"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); ....................................... ....................................... ....................................... // end of the deployment process if (isDeploySuccess()) { instanceElement.outProperties().put("state", "DEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Deployed"); } else { instanceElement.outProperties().put("state", "UNDEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, "Deploy failed!"); } } @Override public void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { var state = instanceElement.outProperties().get("state"); if ("UNDEPLOYED".equals(state)) { // clean up undeployment } else if ("DEPLOYING".equals(state) || "UNDEPLOYING".equals(state)) { // check and clean up } // undeployment process instanceElement.outProperties().put("state", "UNDEPLOYING"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); ....................................... ....................................... ....................................... ....................................... // end of the undeployment process if (isUndeploySuccess()) { instanceElement.outProperties().put("state", "UNDEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.NO_ERROR, "Undeployed"); } else { instanceElement.outProperties().put("state", "DEPLOYED"); intermediaryApi.sendAcElementInfo(instanceElement.instanceId(), instanceElement.elementId(), null, null, instanceElement.outProperties()); intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Undeploy failed!"); } }