Merge pull request #6318 from elsa-workflows/perf/runtime
Optimize performance
This commit is contained in:
commit
7edbdb9bc2
1
.github/workflows/packages.yml
vendored
1
.github/workflows/packages.yml
vendored
|
|
@ -5,6 +5,7 @@ on:
|
|||
branches:
|
||||
- 'main'
|
||||
- 'bug/*'
|
||||
- 'perf/*'
|
||||
release:
|
||||
types: [ prereleased, published ]
|
||||
env:
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ public class Flowchart : Container
|
|||
/// <summary>
|
||||
/// The activity to execute when the flowchart starts.
|
||||
/// </summary>
|
||||
[Port]
|
||||
[Browsable(false)]
|
||||
public IActivity? Start { get; set; }
|
||||
[Port] [Browsable(false)] public IActivity? Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of connections between activities.
|
||||
|
|
@ -98,8 +96,8 @@ public class Flowchart : Container
|
|||
{
|
||||
var workflowExecutionContext = context.WorkflowExecutionContext;
|
||||
var activityIds = Activities.Select(x => x.Id).ToList();
|
||||
var descendantContexts = context.GetDescendents().Where(x => x.ParentActivityExecutionContext == context).ToList();
|
||||
var activityExecutionContexts = descendantContexts.Where(x => activityIds.Contains(x.Activity.Id)).ToList();
|
||||
var children = context.Children;
|
||||
var hasRunningActivityInstances = children.Where(x => activityIds.Contains(x.Activity.Id)).Any(x => x.Status == ActivityStatus.Running);
|
||||
|
||||
var hasPendingWork = workflowExecutionContext.Scheduler.List().Any(workItem =>
|
||||
{
|
||||
|
|
@ -117,8 +115,6 @@ public class Flowchart : Container
|
|||
return ancestors.Any(x => x == context);
|
||||
});
|
||||
|
||||
var hasRunningActivityInstances = activityExecutionContexts.Any(x => x.Status == ActivityStatus.Running);
|
||||
|
||||
return hasRunningActivityInstances || hasPendingWork;
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +182,7 @@ public class Flowchart : Container
|
|||
var executionCount = scope.GetExecutionCount(activity);
|
||||
var haveInboundActivitiesExecuted = inboundActivities.All(x => scope.GetExecutionCount(x) > executionCount);
|
||||
|
||||
if (haveInboundActivitiesExecuted)
|
||||
if (haveInboundActivitiesExecuted)
|
||||
await flowchartContext.ScheduleActivityAsync(activity, OnChildCompletedAsync);
|
||||
}
|
||||
else
|
||||
|
|
@ -231,7 +227,7 @@ public class Flowchart : Container
|
|||
|
||||
if (!hasPendingWork)
|
||||
{
|
||||
var hasFaultedActivities = context.GetActiveChildren().Any(x => x.Status == ActivityStatus.Faulted);
|
||||
var hasFaultedActivities = context.Children.Any(x => x.Status == ActivityStatus.Faulted);
|
||||
|
||||
if (!hasFaultedActivities)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ public partial class ActivityExecutionContext
|
|||
/// <summary>
|
||||
/// Complete the current activity. This should only be called by activities that explicitly suppress automatic-completion.
|
||||
/// </summary>
|
||||
public async ValueTask CompleteActivityAsync(object? result = default)
|
||||
public async ValueTask CompleteActivityAsync(object? result = null)
|
||||
{
|
||||
var outcomes = result as Outcomes;
|
||||
|
||||
|
|
@ -28,8 +28,8 @@ public partial class ActivityExecutionContext
|
|||
return;
|
||||
|
||||
// Cancel any non-completed child activities.
|
||||
var childContexts = WorkflowExecutionContext.ActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == this && x.CanCancelActivity()).ToList();
|
||||
|
||||
var childContexts = Children.Where(x => x.CanCancelActivity()).ToList();
|
||||
|
||||
foreach (var childContext in childContexts)
|
||||
await childContext.CancelActivityAsync();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public partial class ActivityExecutionContext
|
|||
/// <param name="source">The source of the activity. For example, the source file name and line number in case of composite activities.</param>
|
||||
/// <param name="payload">Any contextual data related to this event.</param>
|
||||
/// <returns>Returns the created <see cref="WorkflowExecutionLogEntry"/>.</returns>
|
||||
public WorkflowExecutionLogEntry AddExecutionLogEntry(string eventName, string? message = default, string? source = default, object? payload = default)
|
||||
public WorkflowExecutionLogEntry AddExecutionLogEntry(string eventName, string? message = null, string? source = null, object? payload = null)
|
||||
{
|
||||
var logEntry = new WorkflowExecutionLogEntry(
|
||||
Id,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public partial class ActivityExecutionContext : IExecutionContext, IDisposable
|
|||
private readonly ISystemClock _systemClock;
|
||||
private readonly List<Bookmark> _bookmarks = [];
|
||||
private long _executionCount;
|
||||
private ActivityExecutionContext? _parentActivityExecutionContext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActivityExecutionContext"/> class.
|
||||
|
|
@ -39,7 +40,7 @@ public partial class ActivityExecutionContext : IExecutionContext, IDisposable
|
|||
{
|
||||
_systemClock = systemClock;
|
||||
WorkflowExecutionContext = workflowExecutionContext;
|
||||
ParentActivityExecutionContext = parentActivityExecutionContext;
|
||||
_parentActivityExecutionContext = parentActivityExecutionContext;
|
||||
ExpressionExecutionContext = expressionExecutionContext;
|
||||
Activity = activity;
|
||||
ActivityDescriptor = activityDescriptor;
|
||||
|
|
@ -84,7 +85,15 @@ public partial class ActivityExecutionContext : IExecutionContext, IDisposable
|
|||
/// <summary>
|
||||
/// The parent activity execution context, if any.
|
||||
/// </summary>
|
||||
public ActivityExecutionContext? ParentActivityExecutionContext { get; internal set; }
|
||||
public ActivityExecutionContext? ParentActivityExecutionContext
|
||||
{
|
||||
get => _parentActivityExecutionContext;
|
||||
internal set
|
||||
{
|
||||
_parentActivityExecutionContext = value;
|
||||
_parentActivityExecutionContext?.Children.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The expression execution context.
|
||||
|
|
@ -159,6 +168,8 @@ public partial class ActivityExecutionContext : IExecutionContext, IDisposable
|
|||
/// </summary>
|
||||
/// <remarks>As of tool version 3.0, all activity Ids are already unique, so there's no need to construct a hierarchical ID</remarks>
|
||||
public string NodeId => ActivityNode.NodeId;
|
||||
|
||||
public ISet<ActivityExecutionContext> Children { get; } = new HashSet<ActivityExecutionContext>();
|
||||
|
||||
/// <summary>
|
||||
/// A list of bookmarks created by the current activity.
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ public partial class WorkflowExecutionContext : IExecutionContext
|
|||
/// </summary>
|
||||
public IReadOnlyCollection<ActivityExecutionContext> ActivityExecutionContexts
|
||||
{
|
||||
get => _activityExecutionContexts.ToList();
|
||||
get => _activityExecutionContexts.AsReadOnly();
|
||||
internal set => _activityExecutionContexts = value.ToList();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,50 +197,6 @@ public static partial class ActivityExecutionContextExtensions
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flattened list of the current context's descendants.
|
||||
/// </summary>
|
||||
public static IEnumerable<ActivityExecutionContext> GetDescendents(this ActivityExecutionContext context)
|
||||
{
|
||||
var children = context.WorkflowExecutionContext.ActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == context).ToList();
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
yield return child;
|
||||
|
||||
foreach (var descendent in GetDescendents(child))
|
||||
yield return descendent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flattened list of the current context's immediate active children.
|
||||
/// </summary>
|
||||
public static IEnumerable<ActivityExecutionContext> GetActiveChildren(this ActivityExecutionContext context) =>
|
||||
context.WorkflowExecutionContext.ActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == context);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flattened list of the current context's immediate children.
|
||||
/// </summary>
|
||||
public static IEnumerable<ActivityExecutionContext> GetChildren(this ActivityExecutionContext context) =>
|
||||
context.WorkflowExecutionContext.ActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == context);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flattened list of the current context's descendants.
|
||||
/// </summary>
|
||||
public static IEnumerable<ActivityExecutionContext> GetDescendants(this ActivityExecutionContext context)
|
||||
{
|
||||
var children = context.WorkflowExecutionContext.ActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == context).ToList();
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
yield return child;
|
||||
|
||||
foreach (var descendant in child.GetDescendants())
|
||||
yield return descendant;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a signal up the current hierarchy of ancestors.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ namespace Elsa.Workflows.Models;
|
|||
/// </summary>
|
||||
public class ActivityNode
|
||||
{
|
||||
private readonly List<ActivityNode> _parents = new();
|
||||
private readonly List<ActivityNode> _children = new();
|
||||
private string? _nodeId;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActivityNode"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -23,8 +27,13 @@ public class ActivityNode
|
|||
{
|
||||
get
|
||||
{
|
||||
var ancestorIds = Ancestors().Reverse().Select(x => x.Activity.Id).ToList();
|
||||
return ancestorIds.Any() ? $"{string.Join(":", ancestorIds)}:{Activity.Id}" : Activity.Id;
|
||||
if (_nodeId == null)
|
||||
{
|
||||
var ancestorIds = Ancestors().Reverse().Select(x => x.Activity.Id).ToList();
|
||||
_nodeId = ancestorIds.Any() ? $"{string.Join(":", ancestorIds)}:{Activity.Id}" : Activity.Id;
|
||||
}
|
||||
|
||||
return _nodeId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,12 +50,23 @@ public class ActivityNode
|
|||
/// <summary>
|
||||
/// Gets the parents of this node.
|
||||
/// </summary>
|
||||
public ICollection<ActivityNode> Parents { get; set; } = new List<ActivityNode>();
|
||||
|
||||
public IReadOnlyCollection<ActivityNode> Parents => _parents.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of this node.
|
||||
/// </summary>
|
||||
public ICollection<ActivityNode> Children { get; set; } = new List<ActivityNode>();
|
||||
public ICollection<ActivityNode> Children => _children.AsReadOnly();
|
||||
|
||||
public void AddParent(ActivityNode parent)
|
||||
{
|
||||
_parents.Add(parent);
|
||||
_nodeId = null;
|
||||
}
|
||||
|
||||
public void AddChild(ActivityNode child)
|
||||
{
|
||||
_children.Add(child);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descendants of this node.
|
||||
|
|
@ -85,7 +105,7 @@ public class ActivityNode
|
|||
/// Gets the siblings of this node.
|
||||
/// </summary>
|
||||
public IEnumerable<ActivityNode> Siblings() => Parents.SelectMany(parent => parent.Children);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings and cousins of this node.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -71,12 +71,12 @@ public class ActivityVisitor : IActivityVisitor
|
|||
|
||||
if (childNode == null)
|
||||
{
|
||||
childNode = new ActivityNode(activity, activityPort.PortName);
|
||||
childNode = new(activity, activityPort.PortName);
|
||||
collectedNodes.Add(childNode);
|
||||
}
|
||||
|
||||
childNode.Parents.Add(pair.Node);
|
||||
pair.Node.Children.Add(childNode);
|
||||
childNode.AddParent(pair.Node);
|
||||
pair.Node.AddChild(childNode);
|
||||
collectedActivities.Add(activity);
|
||||
await VisitRecursiveAsync((childNode, activity), visitorContext, cancellationToken);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public class QueueBasedActivityScheduler : IActivityScheduler
|
|||
public ActivityWorkItem Take() => _queue.Dequeue();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ActivityWorkItem> List() => _queue.ToList();
|
||||
public IEnumerable<ActivityWorkItem> List() => _queue;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Any(Func<ActivityWorkItem, bool> predicate) => _queue.Any(predicate);
|
||||
|
|
|
|||
Loading…
Reference in a new issue