Search in sources :

Example 1 with Loop

use of io.seata.saga.statelang.domain.TaskState.Loop in project seata by seata.

the class LoopTaskUtils method getLoopConfig.

/**
 * get Loop Config from State
 *
 * @param context
 * @param currentState
 * @return currentState loop config if satisfied, else {@literal null}
 */
public static Loop getLoopConfig(ProcessContext context, State currentState) {
    if (matchLoop(currentState)) {
        AbstractTaskState taskState = (AbstractTaskState) currentState;
        StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
        StateMachineConfig stateMachineConfig = (StateMachineConfig) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONFIG);
        if (null != taskState.getLoop()) {
            Loop loop = taskState.getLoop();
            String collectionName = loop.getCollection();
            if (StringUtils.isNotBlank(collectionName)) {
                Object expression = ParameterUtils.createValueExpression(stateMachineConfig.getExpressionFactoryManager(), collectionName);
                Object collection = ParameterUtils.getValue(expression, stateMachineInstance.getContext(), null);
                if (collection instanceof Collection && ((Collection) collection).size() > 0) {
                    LoopContextHolder.getCurrent(context, true).setCollection((Collection) collection);
                    return loop;
                }
            }
            LOGGER.warn("State [{}] loop collection param [{}] invalid", currentState.getName(), collectionName);
        }
    }
    return null;
}
Also used : AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) Loop(io.seata.saga.statelang.domain.TaskState.Loop) Collection(java.util.Collection) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance)

Example 2 with Loop

use of io.seata.saga.statelang.domain.TaskState.Loop in project seata by seata.

the class LoopTaskHandlerInterceptor method preProcess.

@Override
public void preProcess(ProcessContext context) throws EngineExecutionException {
    if (context.hasVariable(DomainConstants.VAR_NAME_IS_LOOP_STATE)) {
        StateInstruction instruction = context.getInstruction(StateInstruction.class);
        AbstractTaskState currentState = (AbstractTaskState) instruction.getState(context);
        int loopCounter;
        Loop loop;
        // get loop config
        if (context.hasVariable(DomainConstants.VAR_NAME_CURRENT_COMPEN_TRIGGER_STATE)) {
            // compensate condition should get stateToBeCompensated 's config
            CompensationHolder compensationHolder = CompensationHolder.getCurrent(context, true);
            StateInstance stateToBeCompensated = compensationHolder.getStatesNeedCompensation().get(currentState.getName());
            AbstractTaskState compensateState = (AbstractTaskState) stateToBeCompensated.getStateMachineInstance().getStateMachine().getState(EngineUtils.getOriginStateName(stateToBeCompensated));
            loop = compensateState.getLoop();
            loopCounter = LoopTaskUtils.reloadLoopCounter(stateToBeCompensated.getName());
        } else {
            loop = currentState.getLoop();
            loopCounter = (int) context.getVariable(DomainConstants.LOOP_COUNTER);
        }
        Collection collection = LoopContextHolder.getCurrent(context, true).getCollection();
        Map<String, Object> contextVariables = (Map<String, Object>) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT);
        Map<String, Object> copyContextVariables = new ConcurrentHashMap<>(contextVariables);
        copyContextVariables.put(loop.getElementIndexName(), loopCounter);
        copyContextVariables.put(loop.getElementVariableName(), iterator(collection, loopCounter));
        ((HierarchicalProcessContext) context).setVariableLocally(DomainConstants.VAR_NAME_STATEMACHINE_CONTEXT, copyContextVariables);
    }
}
Also used : Loop(io.seata.saga.statelang.domain.TaskState.Loop) CompensationHolder(io.seata.saga.engine.pcext.utils.CompensationHolder) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) HierarchicalProcessContext(io.seata.saga.proctrl.HierarchicalProcessContext) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Map(java.util.Map) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 3 with Loop

use of io.seata.saga.statelang.domain.TaskState.Loop in project seata by seata.

the class LoopTaskUtils method reloadLoopContext.

/**
 * reload loop counter context while forward
 *
 * @param context
 * @param forwardStateName
 */
public static void reloadLoopContext(ProcessContext context, String forwardStateName) {
    StateMachineInstance stateMachineInstance = (StateMachineInstance) context.getVariable(DomainConstants.VAR_NAME_STATEMACHINE_INST);
    List<StateInstance> actList = stateMachineInstance.getStateList();
    List<StateInstance> forwardStateList = actList.stream().filter(e -> forwardStateName.equals(EngineUtils.getOriginStateName(e))).collect(Collectors.toList());
    LoopContextHolder loopContextHolder = LoopContextHolder.getCurrent(context, true);
    Collection collection = loopContextHolder.getCollection();
    LinkedList<Integer> list = new LinkedList<>();
    for (int i = 0; i < collection.size(); i++) {
        list.addFirst(i);
    }
    int executedNumber = 0;
    LinkedList<Integer> failEndList = new LinkedList<>();
    for (StateInstance stateInstance : forwardStateList) {
        if (!stateInstance.isIgnoreStatus()) {
            if (ExecutionStatus.SU.equals(stateInstance.getStatus())) {
                executedNumber += 1;
            } else {
                stateInstance.setIgnoreStatus(true);
                failEndList.addFirst(reloadLoopCounter(stateInstance.getName()));
            }
            list.remove(Integer.valueOf(reloadLoopCounter(stateInstance.getName())));
        }
    }
    loopContextHolder.getLoopCounterStack().addAll(list);
    loopContextHolder.getForwardCounterStack().addAll(failEndList);
    loopContextHolder.getNrOfInstances().set(collection.size());
    loopContextHolder.getNrOfCompletedInstances().set(executedNumber);
}
Also used : NumberUtils(io.seata.common.util.NumberUtils) ProcessContextImpl(io.seata.saga.proctrl.impl.ProcessContextImpl) LoggerFactory(org.slf4j.LoggerFactory) DomainConstants(io.seata.saga.statelang.domain.DomainConstants) ArrayList(java.util.ArrayList) ExpressionEvaluator(io.seata.saga.engine.evaluation.expression.ExpressionEvaluator) Map(java.util.Map) EvaluatorFactoryManager(io.seata.saga.engine.evaluation.EvaluatorFactoryManager) ExecutionStatus(io.seata.saga.statelang.domain.ExecutionStatus) LinkedList(java.util.LinkedList) ForwardInvalidException(io.seata.saga.engine.exception.ForwardInvalidException) ProcessContext(io.seata.saga.proctrl.ProcessContext) EmptyStackException(java.util.EmptyStackException) Logger(org.slf4j.Logger) CollectionUtils(io.seata.common.util.CollectionUtils) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) StateInstance(io.seata.saga.statelang.domain.StateInstance) StateInstruction(io.seata.saga.engine.pcext.StateInstruction) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) StateMachine(io.seata.saga.statelang.domain.StateMachine) Collectors(java.util.stream.Collectors) FrameworkErrorCode(io.seata.common.exception.FrameworkErrorCode) State(io.seata.saga.statelang.domain.State) List(java.util.List) StateMachineConfig(io.seata.saga.engine.StateMachineConfig) Loop(io.seata.saga.statelang.domain.TaskState.Loop) StringUtils(io.seata.common.util.StringUtils) AbstractTaskState(io.seata.saga.statelang.domain.impl.AbstractTaskState) Collection(java.util.Collection) LinkedList(java.util.LinkedList) StateMachineInstance(io.seata.saga.statelang.domain.StateMachineInstance) StateInstance(io.seata.saga.statelang.domain.StateInstance)

Example 4 with Loop

use of io.seata.saga.statelang.domain.TaskState.Loop 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 5 with Loop

use of io.seata.saga.statelang.domain.TaskState.Loop 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)

Aggregations

Loop (io.seata.saga.statelang.domain.TaskState.Loop)6 StateInstruction (io.seata.saga.engine.pcext.StateInstruction)5 StateMachineInstance (io.seata.saga.statelang.domain.StateMachineInstance)5 ProcessContext (io.seata.saga.proctrl.ProcessContext)4 AbstractTaskState (io.seata.saga.statelang.domain.impl.AbstractTaskState)4 StateMachineConfig (io.seata.saga.engine.StateMachineConfig)3 EngineExecutionException (io.seata.saga.engine.exception.EngineExecutionException)3 StateInstance (io.seata.saga.statelang.domain.StateInstance)3 Collection (java.util.Collection)3 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)3 ForwardInvalidException (io.seata.saga.engine.exception.ForwardInvalidException)2 ProcessContextBuilder (io.seata.saga.engine.utils.ProcessContextBuilder)2 HierarchicalProcessContext (io.seata.saga.proctrl.HierarchicalProcessContext)2 ProcessContextImpl (io.seata.saga.proctrl.impl.ProcessContextImpl)2 ExecutionStatus (io.seata.saga.statelang.domain.ExecutionStatus)2 State (io.seata.saga.statelang.domain.State)2 LoopStartStateImpl (io.seata.saga.statelang.domain.impl.LoopStartStateImpl)2 ArrayList (java.util.ArrayList)2 Map (java.util.Map)2 FrameworkErrorCode (io.seata.common.exception.FrameworkErrorCode)1