elsa-core
elsa-core copied to clipboard
[ENH] Consider joining "active" branches
public class FlowJoinConditionWaitAllWorkflow : IWorkflow
{
public ValueTask BuildAsync(IWorkflowBuilder builder, CancellationToken cancellationToken = default)
{
var switchFlow = new FlowSwitch
{
CanStartWorkflow = true,
Cases = new List<FlowSwitchCase>()
{
new FlowSwitchCase() { Label="Case1" ,Condition = new JavaScriptExpression("1 == 1") },
new FlowSwitchCase() { Label="Case2" ,Condition = new JavaScriptExpression("1 != 1") },
},
Mode = new(SwitchMode.MatchAny)
};
var userTask1 = new AdvancedUserTask()
{
Name = "userTask1",
Cases = new(context => new List<string>() { "下一步userTask2" }),
AssignUser = new(c => "admin123")
};
var userTask2 = new AdvancedUserTask
{
Name = "userTask2",
Cases = new(context => new List<string>() { "下一步userTask3" }),
AssignUser = new(c => "admin456")
};
var flowJoin = new FlowJoin
{
Mode = new(FlowJoinMode.WaitAll)
};
var finish = new Finish
{
};
builder.Root = new Flowchart
{
Activities =
{
switchFlow,
userTask1,
userTask2,
flowJoin,
finish
},
Connections =
{
new Connection(new Endpoint(switchFlow,"Case1"),new Endpoint(userTask1,"In")),
new Connection(new Endpoint(userTask1,"下一步userTask2"),new Endpoint(flowJoin)),
new Connection(new Endpoint(switchFlow,"Case2"),new Endpoint(userTask2,"In")),
new Connection(new Endpoint(userTask2,"下一步userTask3"),new Endpoint(flowJoin)),
new Connection(new Endpoint(flowJoin),new Endpoint(finish)),
}
};
return new ValueTask();
}
}
like this ,Because Case2 will never execute! @sfmskywalker
Hi @Nokecy , is this happening with the latest build?
in elsa 3.0.0-preview.712 @sfmskywalker
Thanks for the info @Nokecy ! I noticed that your sample workflow is using a custom activity called AdvancedUserTask - is that something you share as well? Or, perhaps even better, can you reproduce the problem with built-in activities only?
public class FlowJoinConditionWaitAllBuiltInWorkflow : IWorkflow
{
public ValueTask BuildAsync(IWorkflowBuilder builder, CancellationToken cancellationToken = default)
{
var switchFlow = new FlowSwitch
{
CanStartWorkflow = true,
Cases = new List<FlowSwitchCase>()
{
new FlowSwitchCase() { Label="Case1" ,Condition = new JavaScriptExpression("1 == 1") },
new FlowSwitchCase() { Label="Case2" ,Condition = new JavaScriptExpression("1 != 1") },
},
Mode = new(SwitchMode.MatchAny)
};
var userTask1 = new WriteLine("user task1")
{
Name = "userTask1",
};
var userTask2 = new WriteLine("user task2")
{
Name = "userTask2",
};
var flowJoin = new FlowJoin
{
Mode = new(FlowJoinMode.WaitAll)
};
var finish = new Finish
{
};
builder.Root = new Flowchart
{
Activities =
{
switchFlow,
userTask1,
userTask2,
flowJoin,
finish
},
Connections =
{
new Connection(new Endpoint(switchFlow,"Case1"),new Endpoint(userTask1,"In")),
new Connection(new Endpoint(userTask1,"下一步userTask2"),new Endpoint(flowJoin)),
new Connection(new Endpoint(switchFlow,"Case2"),new Endpoint(userTask2,"In")),
new Connection(new Endpoint(userTask2,"下一步userTask3"),new Endpoint(flowJoin)),
new Connection(new Endpoint(flowJoin),new Endpoint(finish)),
}
};
return new ValueTask();
}
}
@sfmskywalker like this
any idea ? @sfmskywalker
Hi @Nokecy ,
I looked into it, and it seems to me that the behavior is as should be expected:
- The Switch activity produces the Case 1 outcome
- Task 1 executes and completes
- Join (wait all) executes and waits for the Task 2 activity to complete.
- Task 2 will never execute, let alone complete, since the Switch activity already executed and always produces the Case 1 outcome.
Since Join is configured to wait for all inbound activities to complete, it will never complete - this is by design.
Here's a visual representation:
If you do need to wait for any inbound activity to complete, then you need to change the Join Mode from Wait All to Wait Any:
Now, the workflow will complete.
I'll close this issue as "by design", but please feel free to continue the conversation, and to reopen the issue if you still think that there is a problem.
Thank you!
- The Switch activity produces the Case 1 and Case2 outcome
- Task 1 and Task 2 executes and completes
- Task 3 was not executed because the conditions did not meet
- workflow instance never completes
- @sfmskywalker what should i do
Make sure that the JoinMode of the Join activity is set to Wait Any.
I need to ensure that task1 and task2 are completed simultaneously before proceeding to the next node! @sfmskywalker
Both Task 1 and Task 2 will execute, even though Join will not wait for it.
But, if you want to ensure Task 1 and Task 2 have executed before Join continues, you need to add another Join for Task 1 and Task 2 using Wait All, and then connect that Join to the second Join. Task 3 also needs to connect to the second Join. This second Join needs to be Wait Any.
If, on the other hand, you need it to just work without knowing which cases are activated from the Switch activity, then that’s not going to work either. In that case, the Join activity needs to somehow “know” what outcomes of the Switch activity were activated so that it only waits for those branches to complete before continuing. Currently, Join only knows about its immediate inbound activities.
I’ll have to give this scenario some more thought, but if you have a suggestion I’m open to hearing it.
i need this one , but i don't know how to do it! sorry sorry @sfmskywalker
i need it to just work without knowing which cases are activated from the Switch activity
No worries! Let's reopen this issue and let it marinate for a while 😄 Thanks for clarifying the use case!
I received a similar request from a user that essentially tries to model BPMN's Inclusive OR gateway:
In this diagram, the second gateway merges the paths into a single one, provided that all active branches from the OR gateway have completed.
Similarly, we need to allow the Join activity to continue if the active branches have completed. This would also allow the Join to work when merging True/False branches without requiring the WaitAny join mode.
It seems to me that the simplest way to solve this would be for Join to have some notion of what the "switch" activity was so it know how many of incoming branches it needs to wait for. Maybe as an extra property of join (if empty it will wait for all incoming or any incoming), if filled it will wait for as many as were created by the "switch/branch" activity....
Fixed via #6632