Search in sources :

Example 16 with StateInstruction

use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.

the class ServiceTaskStateHandler method process.

@Override
public void process(ProcessContext context) throws EngineExecutionException {
    StateInstruction instruction = context.getInstruction(StateInstruction.class);
    ServiceTaskStateImpl state = (ServiceTaskStateImpl) instruction.getState(context);
    String serviceName = state.getServiceName();
    String methodName = state.getServiceMethod();
    StateInstance stateInstance = (StateInstance) context.getVariable(DomainConstants.VAR_NAME_STATE_INST);
    Object result;
    try {
        List<Object> input = (List<Object>) context.getVariable(DomainConstants.VAR_NAME_INPUT_PARAMS);
        // Set the current task execution status to RU (Running)
        stateInstance.setStatus(ExecutionStatus.RU);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(">>>>>>>>>>>>>>>>>>>>>> Start to execute State[{}], ServiceName[{}], Method[{}], Input:{}", state.getName(), serviceName, methodName, input);
        }
        if (state instanceof CompensateSubStateMachineState) {
            // If it is the compensation of the substate machine,
            // directly call the state machine's compensate method
            result = compensateSubStateMachine(context, state, input, stateInstance, (StateMachineEngine) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_ENGINE));
        } else {
            StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
            ServiceInvoker serviceInvoker = stateMachineConfig.getServiceInvokerManager().getServiceInvoker(state.getServiceType());
            if (serviceInvoker == null) {
                throw new EngineExecutionException("No such ServiceInvoker[" + state.getServiceType() + "]", FrameworkErrorCode.ObjectNotExists);
            }
            if (serviceInvoker instanceof ApplicationContextAware) {
                ((ApplicationContextAware) serviceInvoker).setApplicationContext(stateMachineConfig.getApplicationContext());
            }
            result = serviceInvoker.invoke(state, input.toArray());
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("<<<<<<<<<<<<<<<<<<<<<< State[{}], ServiceName[{}], Method[{}] Execute finish. result: {}", state.getName(), serviceName, methodName, result);
        }
        if (result != null) {
            stateInstance.setOutputParams(result);
            ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_OUTPUT_PARAMS, result);
        }
    } catch (Throwable e) {
        LOGGER.error("<<<<<<<<<<<<<<<<<<<<<< State[{}], ServiceName[{}], Method[{}] Execute failed.", state.getName(), serviceName, methodName, e);
        ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_CURRENT_EXCEPTION, e);
        EngineUtils.handleException(context, state, e);
    }
}
Also used : StateMachineEngine(io.seata.saga.engine.StateMachineEngine) ApplicationContextAware(org.springframework.context.ApplicationContextAware) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) CompensateSubStateMachineState(io.seata.saga.statelang.domain.CompensateSubStateMachineState) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ServiceTaskStateImpl(io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl) ArrayList(java.util.ArrayList) List(java.util.List) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) ServiceInvoker(io.seata.saga.engine.invoker.ServiceInvoker) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 17 with StateInstruction

use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.

the class DbAndReportTcStateLogStore method isUpdateMode.

private boolean isUpdateMode(StateInstance stateInstance, ProcessContext context) {
    DefaultStateMachineConfig stateMachineConfig = (DefaultStateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
    StateInstruction instruction = context.getInstruction(StateInstruction.class);
    ServiceTaskStateImpl state = (ServiceTaskStateImpl) instruction.getState(context);
    StateMachine stateMachine = stateInstance.getStateMachineInstance().getStateMachine();
    if (StringUtils.hasLength(stateInstance.getStateIdRetriedFor())) {
        if (null != state.isRetryPersistModeUpdate()) {
            return state.isRetryPersistModeUpdate();
        } else if (null != stateMachine.isRetryPersistModeUpdate()) {
            return stateMachine.isRetryPersistModeUpdate();
        }
        return stateMachineConfig.isSagaRetryPersistModeUpdate();
    } else if (StringUtils.hasLength(stateInstance.getStateIdCompensatedFor())) {
        // find if this compensate has been executed
        for (int i = 0; i < stateInstance.getStateMachineInstance().getStateList().size(); i++) {
            StateInstance aStateInstance = stateInstance.getStateMachineInstance().getStateList().get(i);
            if (aStateInstance.isForCompensation() && aStateInstance.getName().equals(stateInstance.getName())) {
                if (null != state.isCompensatePersistModeUpdate()) {
                    return state.isCompensatePersistModeUpdate();
                } else if (null != stateMachine.isCompensatePersistModeUpdate()) {
                    return stateMachine.isCompensatePersistModeUpdate();
                }
                return stateMachineConfig.isSagaCompensatePersistModeUpdate();
            }
        }
        return false;
    }
    return false;
}
Also used : StateInstruction(io.seata.saga.engine.pcext.StateInstruction) StateMachine(io.seata.saga.statelang.domain.StateMachine) DefaultStateMachineConfig(io.seata.saga.engine.impl.DefaultStateMachineConfig) ServiceTaskStateImpl(io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 18 with StateInstruction

use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.

the class ProcessCtrlStateMachineEngine method forwardInternal.

protected StateMachineInstance forwardInternal(String stateMachineInstId, Map<String, Object> replaceParams, boolean skip, boolean async, AsyncCallback callback) throws EngineExecutionException {
    StateMachineInstance stateMachineInstance = reloadStateMachineInstance(stateMachineInstId);
    if (stateMachineInstance == null) {
        throw new ForwardInvalidException("StateMachineInstance is not exits", FrameworkErrorCode.StateMachineInstanceNotExists);
    }
    if (ExecutionStatus.SU.equals(stateMachineInstance.getStatus()) && stateMachineInstance.getCompensationStatus() == null) {
        return stateMachineInstance;
    }
    ExecutionStatus[] acceptStatus = new ExecutionStatus[] { ExecutionStatus.FA, ExecutionStatus.UN, ExecutionStatus.RU };
    checkStatus(stateMachineInstance, acceptStatus, null, stateMachineInstance.getStatus(), null, "forward");
    List<StateInstance> actList = stateMachineInstance.getStateList();
    if (CollectionUtils.isEmpty(actList)) {
        throw new ForwardInvalidException("StateMachineInstance[id:" + stateMachineInstId + "] has no stateInstance, pls start a new StateMachine execution instead", FrameworkErrorCode.OperationDenied);
    }
    StateInstance lastForwardState = findOutLastForwardStateInstance(actList);
    if (lastForwardState == null) {
        throw new ForwardInvalidException("StateMachineInstance[id:" + stateMachineInstId + "] Cannot find last forward execution stateInstance", FrameworkErrorCode.OperationDenied);
    }
    ProcessContextBuilder contextBuilder = ProcessContextBuilder.create().withProcessType(ProcessType.STATE_LANG).withOperationName(DomainConstants.OPERATION_NAME_FORWARD).withAsyncCallback(callback).withStateMachineInstance(stateMachineInstance).withStateInstance(lastForwardState).withStateMachineConfig(getStateMachineConfig()).withStateMachineEngine(this);
    contextBuilder.withIsAsyncExecution(async);
    ProcessContext context = contextBuilder.build();
    Map<String, Object> contextVariables = getStateMachineContextVariables(stateMachineInstance);
    if (replaceParams != null) {
        contextVariables.putAll(replaceParams);
    }
    putBusinesskeyToContextariables(stateMachineInstance, contextVariables);
    ConcurrentHashMap<String, Object> concurrentContextVariables = new ConcurrentHashMap<>(contextVariables.size());
    nullSafeCopy(contextVariables, concurrentContextVariables);
    context.setVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT, concurrentContextVariables);
    stateMachineInstance.setContext(concurrentContextVariables);
    String originStateName = EngineUtils.getOriginStateName(lastForwardState);
    State lastState = stateMachineInstance.getStateMachine().getState(originStateName);
    Loop loop = LoopTaskUtils.getLoopConfig(context, lastState);
    if (null != loop && ExecutionStatus.SU.equals(lastForwardState.getStatus())) {
        lastForwardState = LoopTaskUtils.findOutLastNeedForwardStateInstance(context);
    }
    context.setVariable(lastForwardState.getName() + DomainConstants.VAR_NAME_RETRIED_STATE_INST_ID, lastForwardState.getId());
    if (DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(lastForwardState.getType()) && !ExecutionStatus.SU.equals(lastForwardState.getCompensationStatus())) {
        context.setVariable(DomainConstants.VAR_NAME_IS_FOR_SUB_STATMACHINE_FORWARD, true);
    }
    if (!ExecutionStatus.SU.equals(lastForwardState.getStatus())) {
        lastForwardState.setIgnoreStatus(true);
    }
    try {
        StateInstruction inst = new StateInstruction();
        inst.setTenantId(stateMachineInstance.getTenantId());
        inst.setStateMachineName(stateMachineInstance.getStateMachine().getName());
        if (skip || ExecutionStatus.SU.equals(lastForwardState.getStatus())) {
            String next = null;
            State state = stateMachineInstance.getStateMachine().getState(EngineUtils.getOriginStateName(lastForwardState));
            if (state != null && state instanceof AbstractTaskState) {
                next = ((AbstractTaskState) state).getNext();
            }
            if (StringUtils.isEmpty(next)) {
                LOGGER.warn("Last Forward execution StateInstance was succeed, and it has not Next State , skip forward " + "operation");
                return stateMachineInstance;
            }
            inst.setStateName(next);
        } else {
            if (ExecutionStatus.RU.equals(lastForwardState.getStatus()) && !EngineUtils.isTimeout(lastForwardState.getGmtStarted(), stateMachineConfig.getServiceInvokeTimeout())) {
                throw new EngineExecutionException("State [" + lastForwardState.getName() + "] is running, operation[forward] denied", FrameworkErrorCode.OperationDenied);
            }
            inst.setStateName(EngineUtils.getOriginStateName(lastForwardState));
        }
        context.setInstruction(inst);
        stateMachineInstance.setStatus(ExecutionStatus.RU);
        stateMachineInstance.setRunning(true);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Operation [forward] started  stateMachineInstance[id:" + stateMachineInstance.getId() + "]");
        }
        if (stateMachineInstance.getStateMachine().isPersist()) {
            stateMachineConfig.getStateLogStore().recordStateMachineRestarted(stateMachineInstance, context);
        }
        loop = LoopTaskUtils.getLoopConfig(context, inst.getState(context));
        if (null != loop) {
            inst.setTemporaryState(new LoopStartStateImpl());
        }
        if (async) {
            stateMachineConfig.getAsyncProcessCtrlEventPublisher().publish(context);
        } else {
            stateMachineConfig.getProcessCtrlEventPublisher().publish(context);
        }
    } catch (EngineExecutionException e) {
        LOGGER.error("Operation [forward] failed", e);
        throw e;
    }
    return stateMachineInstance;
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) ForwardInvalidException(io.seata.saga.engine.exception.ForwardInvalidException) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ProcessContext(io.seata.saga.proctrl.ProcessContext) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) LoopStartStateImpl(io.seata.saga.statelang.domain.impl.LoopStartStateImpl) ProcessContextBuilder(io.seata.saga.engine.utils.ProcessContextBuilder) ExecutionStatus(io.seata.saga.statelang.domain.ExecutionStatus) State(io.seata.saga.statelang.domain.State) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 19 with StateInstruction

use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.

the class ProcessCtrlStateMachineEngine method startInternal.

private StateMachineInstance startInternal(String stateMachineName, String tenantId, String businessKey, Map<String, Object> startParams, boolean async, AsyncCallback callback) throws EngineExecutionException {
    if (async && !stateMachineConfig.isEnableAsync()) {
        throw new EngineExecutionException("Asynchronous start is disabled. please set StateMachineConfig.enableAsync=true first.", FrameworkErrorCode.AsynchronousStartDisabled);
    }
    if (StringUtils.isEmpty(tenantId)) {
        tenantId = stateMachineConfig.getDefaultTenantId();
    }
    StateMachineInstance instance = createMachineInstance(stateMachineName, tenantId, businessKey, startParams);
    ProcessContextBuilder contextBuilder = ProcessContextBuilder.create().withProcessType(ProcessType.STATE_LANG).withOperationName(DomainConstants.OPERATION_NAME_START).withAsyncCallback(callback).withInstruction(new StateInstruction(stateMachineName, tenantId)).withStateMachineInstance(instance).withStateMachineConfig(getStateMachineConfig()).withStateMachineEngine(this);
    Map<String, Object> contextVariables;
    if (startParams != null) {
        contextVariables = new ConcurrentHashMap<>(startParams.size());
        nullSafeCopy(startParams, contextVariables);
    } else {
        contextVariables = new ConcurrentHashMap<>();
    }
    instance.setContext(contextVariables);
    contextBuilder.withStateMachineContextVariables(contextVariables);
    contextBuilder.withIsAsyncExecution(async);
    ProcessContext processContext = contextBuilder.build();
    if (instance.getStateMachine().isPersist() && stateMachineConfig.getStateLogStore() != null) {
        stateMachineConfig.getStateLogStore().recordStateMachineStarted(instance, processContext);
    }
    if (StringUtils.isEmpty(instance.getId())) {
        instance.setId(stateMachineConfig.getSeqGenerator().generate(DomainConstants.SEQ_ENTITY_STATE_MACHINE_INST));
    }
    StateInstruction stateInstruction = processContext.getInstruction(StateInstruction.class);
    Loop loop = LoopTaskUtils.getLoopConfig(processContext, stateInstruction.getState(processContext));
    if (null != loop) {
        stateInstruction.setTemporaryState(new LoopStartStateImpl());
    }
    if (async) {
        stateMachineConfig.getAsyncProcessCtrlEventPublisher().publish(processContext);
    } else {
        stateMachineConfig.getProcessCtrlEventPublisher().publish(processContext);
    }
    return instance;
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) LoopStartStateImpl(io.seata.saga.statelang.domain.impl.LoopStartStateImpl) ProcessContextBuilder(io.seata.saga.engine.utils.ProcessContextBuilder) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) ProcessContext(io.seata.saga.proctrl.ProcessContext) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance)

Example 20 with StateInstruction

use of io.seata.saga.engine.pcext.StateInstruction in project seata by seata.

the class LoopStartStateHandler 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);
    instruction.setTemporaryState(null);
    Loop loop = LoopTaskUtils.getLoopConfig(context, instruction.getState(context));
    LoopContextHolder loopContextHolder = LoopContextHolder.getCurrent(context, true);
    Semaphore semaphore = null;
    int maxInstances = 0;
    List<ProcessContext> loopContextList = new ArrayList<>();
    if (null != loop) {
        if (!stateMachineConfig.isEnableAsync() || null == stateMachineConfig.getAsyncProcessCtrlEventPublisher()) {
            throw new EngineExecutionException("Asynchronous start is disabled. Loop execution will run asynchronous, please set " + "StateMachineConfig.enableAsync=true first.", FrameworkErrorCode.AsynchronousStartDisabled);
        }
        int totalInstances;
        if (DomainConstants.OPERATION_NAME_FORWARD.equals(context.getVariable(DomainConstants.VAR_NAME_OPERATION_NAME))) {
            LoopTaskUtils.reloadLoopContext(context, instruction.getState(context).getName());
            totalInstances = loopContextHolder.getNrOfInstances().get() - loopContextHolder.getNrOfCompletedInstances().get();
        } else {
            LoopTaskUtils.createLoopCounterContext(context);
            totalInstances = loopContextHolder.getNrOfInstances().get();
        }
        maxInstances = Math.min(loop.getParallel(), totalInstances);
        semaphore = new Semaphore(maxInstances);
        context.setVariable(DomainConstants.LOOP_SEMAPHORE, semaphore);
        context.setVariable(DomainConstants.VAR_NAME_IS_LOOP_STATE, true);
        // publish loop tasks
        for (int i = 0; i < totalInstances; i++) {
            try {
                semaphore.acquire();
                ProcessContextImpl tempContext;
                // fail end inst should be forward without completion condition check
                if (!loopContextHolder.getForwardCounterStack().isEmpty()) {
                    int failEndLoopCounter = loopContextHolder.getForwardCounterStack().pop();
                    tempContext = (ProcessContextImpl) LoopTaskUtils.createLoopEventContext(context, failEndLoopCounter);
                } else if (loopContextHolder.isFailEnd() || LoopTaskUtils.isCompletionConditionSatisfied(context)) {
                    semaphore.release();
                    break;
                } else {
                    tempContext = (ProcessContextImpl) LoopTaskUtils.createLoopEventContext(context, -1);
                }
                if (DomainConstants.OPERATION_NAME_FORWARD.equals(context.getVariable(DomainConstants.VAR_NAME_OPERATION_NAME))) {
                    ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_IS_FOR_SUB_STATMACHINE_FORWARD, LoopTaskUtils.isForSubStateMachineForward(tempContext));
                }
                stateMachineConfig.getAsyncProcessCtrlEventPublisher().publish(tempContext);
                loopContextHolder.getNrOfActiveInstances().incrementAndGet();
                loopContextList.add(tempContext);
            } catch (InterruptedException e) {
                LOGGER.error("try execute loop task for State: [{}] is interrupted, message: [{}]", instruction.getStateName(), e.getMessage());
                throw new EngineExecutionException(e);
            }
        }
    } else {
        LOGGER.warn("Loop config of State [{}] is illegal, will execute as normal", instruction.getStateName());
        instruction.setTemporaryState(instruction.getState(context));
    }
    try {
        if (null != semaphore) {
            boolean isFinished = false;
            while (!isFinished) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("wait {}ms for loop state [{}] finish", AWAIT_TIMEOUT, instruction.getStateName());
                }
                isFinished = semaphore.tryAcquire(maxInstances, AWAIT_TIMEOUT, TimeUnit.MILLISECONDS);
            }
            if (loopContextList.size() > 0) {
                LoopTaskUtils.putContextToParent(context, loopContextList, instruction.getState(context));
            }
        }
    } catch (InterruptedException e) {
        LOGGER.error("State: [{}] wait loop execution complete is interrupted, message: [{}]", instruction.getStateName(), e.getMessage());
        throw new EngineExecutionException(e);
    } finally {
        context.removeVariable(DomainConstants.LOOP_SEMAPHORE);
        context.removeVariable(DomainConstants.VAR_NAME_IS_LOOP_STATE);
        LoopContextHolder.clearCurrent(context);
    }
    if (loopContextHolder.isFailEnd()) {
        String currentExceptionRoute = LoopTaskUtils.decideCurrentExceptionRoute(loopContextList, stateMachineInstance.getStateMachine());
        if (StringUtils.isNotBlank(currentExceptionRoute)) {
            ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_CURRENT_EXCEPTION_ROUTE, currentExceptionRoute);
        } else {
            for (ProcessContext processContext : loopContextList) {
                if (processContext.hasVariable(DomainConstants.VAR_NAME_CURRENT_EXCEPTION)) {
                    Exception exception = (Exception) processContext.getVariable(DomainConstants.VAR_NAME_CURRENT_EXCEPTION);
                    EngineUtils.failStateMachine(context, exception);
                    break;
                }
            }
        }
    }
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) ProcessContextImpl(io.seata.saga.proctrl.impl.ProcessContextImpl) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) ArrayList(java.util.ArrayList) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) Semaphore(java.util.concurrent.Semaphore) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) ProcessContext(io.seata.saga.proctrl.ProcessContext) EngineExecutionException(io.seata.saga.engine.exception.EngineExecutionException) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) LoopContextHolder(io.seata.saga.engine.pcext.utils.LoopContextHolder) StateMachineConfig(io.seata.saga.engine.StateMachineConfig)

Aggregations

StateInstruction (io.seata.saga.engine.pcext.StateInstruction)23 EngineExecutionException (io.seata.saga.engine.exception.EngineExecutionException)15 StateMachineInstance (io.seata.saga.statelang.domain.StateMachineInstance)14 StateMachineConfig (io.seata.saga.engine.StateMachineConfig)11 StateInstance (io.seata.saga.statelang.domain.StateInstance)10 Map (java.util.Map)10 HierarchicalProcessContext (io.seata.saga.proctrl.HierarchicalProcessContext)7 AbstractTaskState (io.seata.saga.statelang.domain.impl.AbstractTaskState)5 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)5 ProcessContext (io.seata.saga.proctrl.ProcessContext)4 Loop (io.seata.saga.statelang.domain.TaskState.Loop)4 ServiceTaskStateImpl (io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl)4 ArrayList (java.util.ArrayList)4 Date (java.util.Date)4 ForwardInvalidException (io.seata.saga.engine.exception.ForwardInvalidException)3 ProcessContextBuilder (io.seata.saga.engine.utils.ProcessContextBuilder)3 CompensateSubStateMachineState (io.seata.saga.statelang.domain.CompensateSubStateMachineState)3 ExecutionStatus (io.seata.saga.statelang.domain.ExecutionStatus)3 State (io.seata.saga.statelang.domain.State)3 StateMachine (io.seata.saga.statelang.domain.StateMachine)3