Retention
This section explains the Retention feature and how it can be used to remove completed workflow instances automatically.
Configuration
To get started with the retention module, you must enable the retention feature.
elsa.UseRetention(r =>
{
r.SweepInterval = TimeSpan.FromMinutes(30);
r.AddDeletePolicy("Delete all finished workflows", _ => new RetentionWorkflowInstanceFilter()
{
WorkflowStatus = WorkflowStatus.Finished
});
});
The SweepInterval
determines how often the retention feature will check for workflows that match any of the configured policies. By default, the retention module provides an AddDeletePolicy
method, which deletes workflow instances that match the given RetentionWorkflowInstanceFilter
.
Note that the policy takes a function that returns a RetentionWorkflowInstanceFilter
.
This function is called during every SweepInterval
.
Example
Delete a workflow if it has been Finished
for over an hour:
elsa.UseRetention(r =>
{
r.SweepInterval = TimeSpan.FromSeconds(30);
r.AddDeletePolicy("Delete all finished workflows", sp =>
{
ISystemClock clock = sp.GetRequiredService<ISystemClock>();
DateTimeOffset threshold = clock.UtcNow.Subtract(TimeSpan.FromHours(1));
return new RetentionWorkflowInstanceFilter()
{
TimestampFilters =
[
new TimestampFilter()
{
Column = nameof(WorkflowInstance.FinishedAt),
Operator = TimestampFilterOperator.LessThanOrEqual,
Timestamp = threshold
}
],
WorkflowStatus = WorkflowStatus.Finished
};
});
});
In this example, we use Elsa's provided ISystemClock
to access the current time and filter workflow instances that finished more than an hour ago. Since the filter is re-created during each SweepInterval
, we can use clock.UtcNow
to calculate the threshold dynamically.
Extending
The retention module allows for easy extension to add new types of policies or to include additional entities in the existing policies.
Extra Entities
Let's say you have custom WorkflowInstanceData
created with every workflow instance that also needs to be removed.
Entity Collector
First, define an IRelatedEntityCollector<TEntity>
that, given a set of workflow instances, returns the related WorkflowInstanceData
records.
public class WorkflowInstanceDataRecordCollector(WorkflowInstanceDataDbContext store) : IRelatedEntityCollector<WorkflowInstanceData>
{
public async IAsyncEnumerable<ICollection<WorkflowInstanceData>> GetRelatedEntities(ICollection<WorkflowInstance> workflowInstances)
{
// TODO: Get WorkflowInstanceData for the given workflowInstances
}
}
Cleanup Strategy
Next, define a cleanup strategy for each policy you wish to support. In this example, we’ll implement a strategy to delete WorkflowInstanceData
records.
public class DeleteWorkflowInstanceDataRecordStrategy(WorkflowInstanceDataDbContext store, ILogger<DeleteWorkflowInstanceDataRecordStrategy> logger) : IDeletionCleanupStrategy<WorkflowInstanceData>
{
public async Task Cleanup(ICollection<WorkflowInstanceData> collection)
{
// TODO: Delete WorkflowInstanceData
}
}
Register Dependencies
Finally, register the DeleteWorkflowInstanceDataRecordStrategy
and WorkflowInstanceDataRecordCollector
in the dependency container:
Services.AddScoped<IDeletionCleanupStrategy<WorkflowInstanceData>, DeleteWorkflowInstanceDataRecordStrategy>();
Services.AddScoped<IRelatedEntityCollector<WorkflowInstanceData>, WorkflowInstanceDataRecordCollector>();
Different Cleanup Strategies
Another way to extend the retention feature is by using different cleanup strategies. For example, you could archive workflow instances to a different storage provider.
Defining a Marker Interface
First, create a marker interface for the archiving cleanup strategy:
public interface IArchivingStrategy<TEntity> : ICleanupStrategy<TEntity>
{
}
Defining the Policy
Next, define a policy
/// <summary>
/// A policy that archives the workflow instance and its related entities.
/// </summary>
public class ArchivingRetentionPolicy(string name, Func<IServiceProvider, RetentionWorkflowInstanceFilter> filter) : IRetentionPolicy
{
public string Name { get; } = name;
public Func<IServiceProvider, RetentionWorkflowInstanceFilter> FilterFactory { get; } = filter;
public Type CleanupStrategy => typeof(IArchivingStrategy<>);
}
Note: In the CleanupStrategy
property, we specify our marker interface (IArchivingStrategy
). This allows the retention module to scan for all implementations of IArchivingStrategy
when executing the ArchivingRetentionPolicy
.
Implementing the Strategy
Next, implement the IArchivingStrategy
for each entity that needs to be archived. The implementation is similar to the CleanupStrategy
in the extra entities example, but instead of deleting the entities, you can move them to a different storage provider.
When implementing a new policy, ensure that you provide an ICleanupStrategy<TEntity>
for the following entities:
ActivityExecutionRecord
StoredBookmark
WorkflowExecutionLogRecord
WorkflowInstance
Last updated