sclasen / swfsm

[golang] simple workflow finite state machines
MIT License
53 stars 14 forks source link

Feature request: Create an `OnEvent` function similar to `OnData` #160

Open ryanwalls opened 8 years ago

ryanwalls commented 8 years ago

I have a case where I need to retry a failed activity task, but only if it fails for a specific reason.

Currently, I've solved this with the following code:

func OnActivityFailedWithRecoverableError(currentActivity string, deciders ...fsm.Decider) fsm.Decider {
    return func(ctx *fsm.FSMContext, h *swf.HistoryEvent, data interface{}) fsm.Outcome {
        if IsRecoverableEvent(ctx, h, data) {
            return fsm.NewComposedDecider(deciders...)(ctx, h, data)
        }
        return ctx.Pass()
    }
}

IsRecoverableEvent just returns a bool.

Instead of doing it this way, I thought about creating an OnEvent function similar to OnData that took a a new type of predicate function that takes in the context, last event, and state data.

Thoughts?

danp commented 8 years ago

Where/how do you use currentActivity?

ryanwalls commented 8 years ago

I have a decider that I use whenever I have a serial list of activities I need to run. It has several custom deciders in it, but it will give you an idea of how I'm using currentActivity.

var WaitingForActivityDecider = func(currentActivity, nextActivity string,
    workflowConfig *config.WorkflowConfig) fsm.Decider {
    return fsm.NewComposedDecider(
        fsm.OnActivityCompleted(currentActivity, fsm.UpdateState(UpdateStateFromPreviousResult),
            fsm.UpdateState(WriteSimulationOutputs),
            OnLastActivity(currentActivity, fsm.UpdateState(SetCompletedDatabaseState), fsm.CompleteWorkflow()),
            fsm.AddDecision(ScheduleActivityDecisionFunc(nextActivity, workflowConfig)),
            fsm.AddDecisions(ScheduleExperimentalActivityDecisionsFunc(nextActivity, workflowConfig)),
            fsm.UpdateState(UpdateDatabaseState(workflowConfig)),
            fsm.Transition(nextActivity)),
        OnActivityFailedWithRecoverableError(currentActivity,
            fsm.AddDecision(ScheduleRetryTimer()), fsm.Stay()),
        OnAnyTimerFired(fsm.AddDecision(RetryActivityFromTimer), fsm.Stay()),
        fsm.OnActivityFailed(currentActivity, fsm.AddDecision(FailWorkflow), fsm.UpdateState(SetErrorDatabaseState)),
        fsm.OnWorkflowCancelRequested(fsm.AddDecisions(CancelOutstandingActivities),
            fsm.Transition(ActivitiesCancelledStateName)),
        fsm.DefaultDecider(),
    )
}

I use this decider when I'm setting up my states. e.g.

thermalSolverState := &fsm.FSMState{
    Name: thermalSolverStateName, 
    Decider: fsmadditions.WaitingForActivityDecider(thermalSolverStateName, mechanicsSolverStateName, config.SimulationWorkflow),
}