use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.
the class ServiceTaskHandlerInterceptor method preProcess.
@Override
public void preProcess(ProcessContext context) throws EngineExecutionException {
StateInstruction instruction = context.getInstruction(StateInstruction.class);
StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
if (EngineUtils.isTimeout(stateMachineInstance.getGmtUpdated(), stateMachineConfig.getTransOperationTimeout())) {
String message = "Saga Transaction [stateMachineInstanceId:" + stateMachineInstance.getId() + "] has timed out, stop execution now.";
LOGGER.error(message);
EngineExecutionException exception = ExceptionUtils.createEngineExecutionException(null, FrameworkErrorCode.StateMachineExecutionTimeout, message, stateMachineInstance, instruction.getStateName());
EngineUtils.failStateMachine(context, exception);
throw exception;
}
StateInstanceImpl stateInstance = new StateInstanceImpl();
Map<String, Object> contextVariables = (Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT);
ServiceTaskStateImpl state = (ServiceTaskStateImpl) instruction.getState(context);
List<Object> serviceInputParams = null;
if (contextVariables != null) {
try {
serviceInputParams = ParameterUtils.createInputParams(stateMachineConfig.getExpressionFactoryManager(), stateInstance, state, contextVariables);
} catch (Exception e) {
String message = "Task [" + state.getName() + "] input parameters assign failed, please check 'Input' expression:" + e.getMessage();
EngineExecutionException exception = ExceptionUtils.createEngineExecutionException(e, FrameworkErrorCode.VariablesAssignError, message, stateMachineInstance, state.getName());
EngineUtils.failStateMachine(context, exception);
throw exception;
}
}
((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_INPUT_PARAMS, serviceInputParams);
stateInstance.setMachineInstanceId(stateMachineInstance.getId());
stateInstance.setStateMachineInstance(stateMachineInstance);
Object isForCompensation = state.isForCompensation();
if (context.hasVariable(DomainConstants.VAR_NAME_IS_LOOP_STATE) && !Boolean.TRUE.equals(isForCompensation)) {
stateInstance.setName(LoopTaskUtils.generateLoopStateName(context, state.getName()));
StateInstance lastRetriedStateInstance = LoopTaskUtils.findOutLastRetriedStateInstance(stateMachineInstance, stateInstance.getName());
stateInstance.setStateIdRetriedFor(lastRetriedStateInstance == null ? null : lastRetriedStateInstance.getId());
} else {
stateInstance.setName(state.getName());
stateInstance.setStateIdRetriedFor((String) context.getVariable(state.getName() + DomainConstants.VAR_NAME_RETRIED_STATE_INST_ID));
}
stateInstance.setGmtStarted(new Date());
stateInstance.setGmtUpdated(stateInstance.getGmtStarted());
stateInstance.setStatus(ExecutionStatus.RU);
if (StringUtils.hasLength(stateInstance.getBusinessKey())) {
((Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT)).put(state.getName() + DomainConstants.VAR_NAME_BUSINESSKEY, stateInstance.getBusinessKey());
}
stateInstance.setType(state.getType());
stateInstance.setForUpdate(state.isForUpdate());
stateInstance.setServiceName(state.getServiceName());
stateInstance.setServiceMethod(state.getServiceMethod());
stateInstance.setServiceType(state.getServiceType());
if (isForCompensation != null && (Boolean) isForCompensation) {
CompensationHolder compensationHolder = CompensationHolder.getCurrent(context, true);
StateInstance stateToBeCompensated = compensationHolder.getStatesNeedCompensation().get(state.getName());
if (stateToBeCompensated != null) {
stateToBeCompensated.setCompensationState(stateInstance);
stateInstance.setStateIdCompensatedFor(stateToBeCompensated.getId());
} else {
LOGGER.error("Compensation State[{}] has no state to compensate, maybe this is a bug.", state.getName());
}
CompensationHolder.getCurrent(context, true).addForCompensationState(stateInstance.getName(), stateInstance);
}
if (DomainConstants.OPERATION_NAME_FORWARD.equals(context.getVariable(DomainConstants.VAR_NAME_OPERATION_NAME)) && StringUtils.isEmpty(stateInstance.getStateIdRetriedFor()) && !state.isForCompensation()) {
List<StateInstance> stateList = stateMachineInstance.getStateList();
if (CollectionUtils.isNotEmpty(stateList)) {
for (int i = stateList.size() - 1; i >= 0; i--) {
StateInstance executedState = stateList.get(i);
if (stateInstance.getName().equals(executedState.getName())) {
stateInstance.setStateIdRetriedFor(executedState.getId());
executedState.setIgnoreStatus(true);
break;
}
}
}
}
stateInstance.setInputParams(serviceInputParams);
if (stateMachineInstance.getStateMachine().isPersist() && state.isPersist() && stateMachineConfig.getStateLogStore() != null) {
try {
stateMachineConfig.getStateLogStore().recordStateStarted(stateInstance, context);
} catch (Exception e) {
String message = "Record state[" + state.getName() + "] started failed, stateMachineInstance[" + stateMachineInstance.getId() + "], Reason: " + e.getMessage();
EngineExecutionException exception = ExceptionUtils.createEngineExecutionException(e, FrameworkErrorCode.ExceptionCaught, message, stateMachineInstance, state.getName());
EngineUtils.failStateMachine(context, exception);
throw exception;
}
}
if (StringUtils.isEmpty(stateInstance.getId())) {
stateInstance.setId(stateMachineConfig.getSeqGenerator().generate(DomainConstants.SEQ_ENTITY_STATE_INST));
}
stateMachineInstance.putStateInstance(stateInstance.getId(), stateInstance);
((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_STATE_INST, stateInstance);
}
use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.
the class LoopTaskUtils method isForSubStateMachineForward.
/**
* forward with subStateMachine should check each loop state's status
*
* @param context
* @return
*/
public static boolean isForSubStateMachineForward(ProcessContext context) {
StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
StateInstruction instruction = context.getInstruction(StateInstruction.class);
StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
StateInstance lastRetriedStateInstance = LoopTaskUtils.findOutLastRetriedStateInstance(stateMachineInstance, LoopTaskUtils.generateLoopStateName(context, instruction.getStateName()));
if (null != lastRetriedStateInstance && DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(lastRetriedStateInstance.getType()) && !ExecutionStatus.SU.equals(lastRetriedStateInstance.getCompensationStatus())) {
while (StringUtils.isNotBlank(lastRetriedStateInstance.getStateIdRetriedFor())) {
lastRetriedStateInstance = stateMachineConfig.getStateLogStore().getStateInstance(lastRetriedStateInstance.getStateIdRetriedFor(), lastRetriedStateInstance.getMachineInstanceId());
}
List<StateMachineInstance> subInst = stateMachineConfig.getStateLogStore().queryStateMachineInstanceByParentId(EngineUtils.generateParentId(lastRetriedStateInstance));
if (CollectionUtils.isNotEmpty(subInst)) {
if (ExecutionStatus.SU.equals(subInst.get(0).getCompensationStatus())) {
return false;
}
}
if (ExecutionStatus.UN.equals(subInst.get(0).getCompensationStatus())) {
throw new ForwardInvalidException("Last forward execution state instance is SubStateMachine and compensation status is " + "[UN], Operation[forward] denied, stateInstanceId:" + lastRetriedStateInstance.getId(), FrameworkErrorCode.OperationDenied);
}
return true;
}
return false;
}
use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.
the class LoopTaskUtils method copyInstruction.
private static StateInstruction copyInstruction(StateInstruction instruction) {
StateInstruction targetInstruction = new StateInstruction();
targetInstruction.setStateMachineName(instruction.getStateMachineName());
targetInstruction.setTenantId(instruction.getTenantId());
targetInstruction.setStateName(instruction.getStateName());
targetInstruction.setTemporaryState(instruction.getTemporaryState());
return targetInstruction;
}
use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.
the class LoopTaskUtils method isCompletionConditionSatisfied.
/**
* check if satisfied completion condition
*
* @param context
* @return
*/
public static boolean isCompletionConditionSatisfied(ProcessContext context) {
StateInstruction instruction = context.getInstruction(StateInstruction.class);
AbstractTaskState currentState = (AbstractTaskState) instruction.getState(context);
LoopContextHolder currentLoopContext = LoopContextHolder.getCurrent(context, true);
if (currentLoopContext.isCompletionConditionSatisfied()) {
return true;
}
int nrOfInstances = currentLoopContext.getNrOfInstances().get();
int nrOfActiveInstances = currentLoopContext.getNrOfActiveInstances().get();
int nrOfCompletedInstances = currentLoopContext.getNrOfCompletedInstances().get();
if (!currentLoopContext.isCompletionConditionSatisfied()) {
synchronized (currentLoopContext) {
if (!currentLoopContext.isCompletionConditionSatisfied()) {
Map<String, Object> stateMachineContext = (Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT);
// multi-instance variables should be double/float while evaluate
stateMachineContext.put(DomainConstants.NUMBER_OF_INSTANCES, (double) nrOfInstances);
stateMachineContext.put(DomainConstants.NUMBER_OF_ACTIVE_INSTANCES, (double) nrOfActiveInstances);
stateMachineContext.put(DomainConstants.NUMBER_OF_COMPLETED_INSTANCES, (double) nrOfCompletedInstances);
if (nrOfCompletedInstances >= nrOfInstances || getEvaluator(context, currentState.getLoop().getCompletionCondition()).evaluate(stateMachineContext)) {
currentLoopContext.setCompletionConditionSatisfied(true);
}
}
}
}
return currentLoopContext.isCompletionConditionSatisfied();
}
use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.
the class CompensationTriggerStateHandler method process.
@Override
public void process(ProcessContext context) throws EngineExecutionException {
StateInstruction instruction = context.getInstruction(StateInstruction.class);
StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
List<StateInstance> stateInstanceList = stateMachineInstance.getStateList();
if (CollectionUtils.isEmpty(stateInstanceList)) {
stateInstanceList = stateMachineConfig.getStateLogStore().queryStateInstanceListByMachineInstanceId(stateMachineInstance.getId());
}
List<StateInstance> stateListToBeCompensated = CompensationHolder.findStateInstListToBeCompensated(context, stateInstanceList);
if (CollectionUtils.isNotEmpty(stateListToBeCompensated)) {
// Clear exceptions that occur during forward execution
Exception e = (Exception) context.removeVariable(DomainConstants.VAR_NAME_CURRENT_EXCEPTION);
if (e != null) {
stateMachineInstance.setException(e);
}
Stack<StateInstance> stateStackToBeCompensated = CompensationHolder.getCurrent(context, true).getStateStackNeedCompensation();
stateStackToBeCompensated.addAll(stateListToBeCompensated);
// and the forward state should not be modified.
if (stateMachineInstance.getStatus() == null || ExecutionStatus.RU.equals(stateMachineInstance.getStatus())) {
stateMachineInstance.setStatus(ExecutionStatus.UN);
}
// Record the status of the state machine as "compensating", and the subsequent routing logic will route
// to the compensation state
stateMachineInstance.setCompensationStatus(ExecutionStatus.RU);
context.setVariable(DomainConstants.VAR_NAME_CURRENT_COMPEN_TRIGGER_STATE, instruction.getState(context));
} else {
EngineUtils.endStateMachine(context);
}
}
Aggregations