elsa-workflows / elsa-workflows/elsa-core

Execution behavior between ForEach and ParallelForEach activity bodies differs

Open
#6,617 0 comments 0 reactions 0 assignees View on GitHub
triaged
Dominant language
C#
Stars
7.9k
Forks
1.5k
Avg merge
15h 22m
Merged PRs (30d)
114

Description

## *Description:*

In elsa 3.3+, when using the ParallelForEach activity, the execution behavior of the activities inside the Body differs from that of the standard ForEach.

In ForEach, each item is processed sequentially, and all activities within the body are executed for each item before moving to the next.

However, in ParallelForEach, instead of executing the full set of body activities for each item in parallel, it appears to execute the first activity in the body across all items, then the second activity across all items, and so on — as if the body activities are being batched rather than grouped per item.

This behavior breaks expected item-level processing logic and makes it difficult to use ParallelForEach as a drop-in replacement for ForEach in scenarios requiring parallelism.

Expected Behavior:
Each item should execute all body activities in parallel, independently from other items.

**Attachments**:
```yaml
- id: Act4
name: Activity 4
type: Elsa.ForEach # Elsa.ParallelForEach
items:
typeName: List
expression:
type: CSharp
value: |
new List { "i0","i1", "i2", "i3", "i4" }
currentValue:
typeName: Object
memoryReference:
id: v1
body:
id: Act5
type: Elsa.Flowchart
customProperties:
runAsynchronously: true
variables:
- id: v2
name: IterationItem
typeName: Object
isArray: false
storageDriverTypeName: 'Elsa.Workflows.MemoryStorageDriver, Elsa.Workflows.Core'
activities:
- id: Act6
type: Elsa.WriteLine
text:
typeName: Object
expression:
type: CSharp
value: Variable.GetCurrentIndex("Act5")
- id: Act7
type: Elsa.SetVariable
variable:
id: v2
value:
typeName: String
expression:
type: CSharp
value: |
Variable.GetCurrentValue("Act5")
- id: Act8
type: Elsa.WriteLine
text:
typeName: Object
expression:
type: CSharp
value: |
Variable.Get("IterationItem")
connections:
- source:
activity: Act6
port: Done
target:
activity: Act7
port: In
- source:
activity: Act7
port: Done
target:
activity: Act8
port: In
```

4. **Reproduction Rate**: "every time"

5. **Video/Screenshots**:
ForEach: result
0
i0
1
i1
2
i2
3
i3
4
i4

ParallelForEach: result
0
1
2
3
4
i0
i1
i2
i3
i4

6. **Additional Configuration**:
extension added for getting current iteration context values
```cs
public static class ContextExtensions
{
public static TValue GetIterationValue(this object context, string activityId, string variableName)
{
ActivityExecutionContext? activityExecutionContext = GetActivityContext(context, activityId);

var currentItem = activityExecutionContext
.GetVariable(variableName);
if (currentItem == null)
{
throw new InvalidOperationException($"{variableName} property not found.");
}

return currentItem;
}

private static ActivityExecutionContext? GetActivityContext(object context, string activityId)
{
var executionContextInfo = context.GetType().GetProperty("ExecutionContext");
if (executionContextInfo == null)
{
throw new InvalidOperationException("ExecutionContext property not found.");
}
var proxy = executionContextInfo.GetValue(context) as ExecutionContextProxy;
var currentExecutionContext = proxy.ExpressionExecutionContext.GetActivityExecutionContext();
var activityExecutionContext = currentExecutionContext.GetAncestors()
.FirstOrDefault(x => x.Activity.Id == activityId);
return activityExecutionContext;
}
}
```

## Expected Behavior
in ParallelForEach, entire body activities execute in group, but the group may occur in random order as below:
2
i2
0
i0
1
i1
4
i4
3
i3

elsa version: 3.3

Please clarify the understanding for this scenario. Thanks.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the YAML example with ForEach and ParallelForEach, focusing on the ParallelForEach activity and its Flowchart body. Compare the observed output with the expected item-grouped execution, allowing item groups to complete in any order; done means each item's full body runs together rather than activity stages being batched across items.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.