elsa-core/src/modules/Elsa.Workflows.Runtime/Services/ActivityExecutionService.cs
Sipke Schoorstra 4266421a4c
Refactor Fault Propagation and Simplify Bookmark Management (#6545)
* Refactor bookmark management and add new features

Streamlined bookmark handling by eliminating temporary storage in `ActivityExecutionContext` and directly managing bookmarks in `WorkflowExecutionContext`. Documented architectural decisions using ADRs.

* Regenerate EF Core migrations

* Refactor fault tracking to use AggregatedFaultCount property.

Replaces FaultCount with AggregatedFaultCount across the codebase to improve clarity and consistency in fault tracking. Updates related methods, properties, and data mappings to align with the new terminology. Fixes initialization issue with nullable inputs in Fault class.

* Add migration to track fault counts in runtime (V3.5)

This migration adds a new column, "AggregatedFaultCount," to the "ActivityExecutionRecords" table. The column is an integer, non-nullable, with a default value of 0, and enables tracking aggregated fault occurrences. The migration also includes a rollback to remove this column if needed.

* Fix typo in ADR 0004 regarding bookmark management convention

Corrected a spelling mistake in the ADR documentation by changing "determins" to "determines." This ensures clarity and maintains the professionalism of the document. No functional changes were made.

* Refine fault propagation logic for child-parent activities

Replace automatic fault transitions of parent activities with an aggregate fault count for descendant activities. This avoids premature state changes while still indicating child activity faults, improving workflow resilience and accuracy.

* Remove signal-driven fault propagation ADR and renumber bookmarks ADR

The ADR for signal-driven fault propagation was deleted, and the direct bookmark management ADR was renamed and renumbered accordingly. Related references in the table of contents, graph, and solution file were updated to reflect these changes.

* Refactor DeleteBookmarks to improve readability.

Reformatted the BookmarkFilter initialization for better clarity and maintainability. This change ensures the code is more aligned with modern C# conventions and improves overall readability. No behavior or functionality has been altered.

* Refactor naming for "AggregatedFaultCount" to "AggregateFaultCount"

Standardized the terminology across the codebase and migrations by renaming all references of "AggregatedFaultCount" to "AggregateFaultCount" for improved consistency and readability. Updated relevant logic, models, migrations, and database contexts accordingly.
2025-04-10 15:01:49 +02:00

56 lines
2.3 KiB
C#

using Elsa.Common.Entities;
using Elsa.Workflows.Runtime.Filters;
using Elsa.Workflows.Runtime.OrderDefinitions;
namespace Elsa.Workflows.Runtime;
/// <inheritdoc />
public class ActivityExecutionStatsService : IActivityExecutionStatsService
{
private readonly IActivityExecutionStore _store;
/// <summary>
/// Initializes a new instance of the <see cref="ActivityExecutionStatsService"/> class.
/// </summary>
public ActivityExecutionStatsService(IActivityExecutionStore store)
{
_store = store;
}
/// <inheritdoc />
public async Task<IEnumerable<ActivityExecutionStats>> GetStatsAsync(string workflowInstanceId, IEnumerable<string> activityNodeIds, CancellationToken cancellationToken = default)
{
var filter = new ActivityExecutionRecordFilter
{
WorkflowInstanceId = workflowInstanceId,
ActivityNodeIds = activityNodeIds?.ToList()
};
var order = new ActivityExecutionRecordOrder<DateTimeOffset>(x => x.StartedAt, OrderDirection.Ascending);
var records = (await _store.FindManySummariesAsync(filter, order, cancellationToken)).ToList();
var groupedRecords = records.GroupBy(x => x.ActivityNodeId).ToList();
var stats = groupedRecords.Select(grouping => new ActivityExecutionStats
{
ActivityNodeId = grouping.Key,
ActivityId = grouping.First().ActivityId,
StartedCount = grouping.Count(),
CompletedCount = grouping.Count(x => x.CompletedAt != null),
UncompletedCount = grouping.Count(x => x.CompletedAt == null),
IsBlocked = grouping.Any(x => x.HasBookmarks),
IsFaulted = grouping.Any(x => x.Status == ActivityStatus.Faulted),
AggregateFaultCount = grouping.Last().AggregateFaultCount
}).ToList();
return stats;
}
/// <inheritdoc />
public async Task<ActivityExecutionStats> GetStatsAsync(string workflowInstanceId, string activityNodeId, CancellationToken cancellationToken = default)
{
var stats = (await GetStatsAsync(workflowInstanceId, [activityNodeId], cancellationToken)).FirstOrDefault();
return stats ?? new ActivityExecutionStats
{
ActivityNodeId = activityNodeId
};
}
}