Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a0e6a4a
Distributed locks to prevent multiple pods executing Reload and Refresh
lukhipolito Feb 19, 2026
3cb85ca
Update src/modules/Elsa.Workflows.Runtime/Services/WorkflowDefinition…
lukhipolito-nexxbiz Feb 19, 2026
d6e28f0
Update src/modules/Elsa.Workflows.Runtime/Services/WorkflowDefinition…
lukhipolito-nexxbiz Feb 19, 2026
3aa143b
Update src/modules/Elsa.Workflows.Runtime/Services/WorkflowDefinition…
lukhipolito-nexxbiz Feb 19, 2026
fcb7b04
Moving distributed lock decorators to proper folder
lukhipolito Feb 19, 2026
5fea6f2
Adding logging for missing lock
lukhipolito Feb 19, 2026
0f648b6
Fixing logger use in distributed classes
lukhipolito Feb 19, 2026
789d8e8
Update src/modules/Elsa.Workflows.Runtime/Distributed/WorkflowDefinit…
lukhipolito-nexxbiz Feb 19, 2026
c8c6b48
Update src/modules/Elsa.Workflows.Runtime/Distributed/WorkflowDefinit…
lukhipolito-nexxbiz Feb 19, 2026
2521c3f
Update src/modules/Elsa.Workflows.Runtime/Distributed/WorkflowDefinit…
lukhipolito-nexxbiz Feb 19, 2026
d4bfe8a
Update src/modules/Elsa.Workflows.Runtime/Distributed/WorkflowDefinit…
lukhipolito-nexxbiz Feb 19, 2026
a8cae08
Update src/modules/Elsa.Workflows.Runtime/Distributed/WorkflowDefinit…
lukhipolito-nexxbiz Feb 19, 2026
444e248
Update src/modules/Elsa.Workflows.Runtime/Distributed/WorkflowDefinit…
lukhipolito-nexxbiz Feb 19, 2026
84d335d
Moving and renaming for clearer purpose
lukhipolito Feb 19, 2026
348559a
Fixing build after file moves
lukhipolito Feb 19, 2026
33cb21b
Addressing workflow refresher empty query leaking distributed lock logic
lukhipolito Feb 20, 2026
b0abe55
Merge branch 'release/3.6.0' into feat/multi-trigger-publish-update
lukhipolito Feb 20, 2026
c3d4e67
Merge branch 'release/3.6.0' into feat/multi-trigger-publish-update
lukhipolito Feb 23, 2026
7279619
Add status to WorkflowRefresherResponse model for in progress operation
lukhipolito Feb 23, 2026
37915ed
Merge branch 'release/3.6.0' into feat/multi-trigger-publish-update
lukhipolito Feb 23, 2026
82fd7e3
Removing typo, fixing build
lukhipolito Feb 23, 2026
0618f52
Update src/modules/Elsa.Workflows.Runtime.Distributed/Services/Distri…
lukhipolito-nexxbiz Feb 23, 2026
5bf7538
Refactor `RefreshWorkflowDefinitionsAsync` for lock acquisition logic…
sfmskywalker Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Elsa.Workflows.Runtime.Requests;
using Elsa.Workflows.Runtime.Responses;
using JetBrains.Annotations;
using Medallion.Threading;

namespace Elsa.Workflows.Runtime.Distributed;

/// <summary>
/// Decorator class that adds Distributed Locking to the Workflow Definitions Refresher.
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
/// </summary>
/// <param name="inner"></param>
/// <param name="distributedLockProvider"></param>
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
[UsedImplicitly]
public class WorkflowDefinitionsRefresherDistributedLocking(IWorkflowDefinitionsRefresher inner,
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
IDistributedLockProvider distributedLockProvider) : IWorkflowDefinitionsRefresher
{
/// <summary>
/// This ensures that only one instance of the application can refresh a set of workflow definitions at a time, preventing potential conflicts and ensuring consistency across distributed environments.
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
public async Task<RefreshWorkflowDefinitionsResponse> RefreshWorkflowDefinitionsAsync(RefreshWorkflowDefinitionsRequest request, CancellationToken cancellationToken = default)
{
if (request.DefinitionIds == null
|| request.DefinitionIds.Count < 1)
{
return new RefreshWorkflowDefinitionsResponse(Array.Empty<string>(), Array.Empty<string>());
}
var definitionIdsKey = string.Join(",", request.DefinitionIds!.OrderBy(x => x));
var lockKey = $"WorkflowDefinitionsRefresher:{definitionIdsKey}";
await using var distributedLock = await distributedLockProvider.TryAcquireLockAsync(
lockKey,
TimeSpan.FromMinutes(1),
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
cancellationToken);

if (distributedLock == null)
{
// Another instance is already refreshing these workflow definitions. Skip this refresh.
return new RefreshWorkflowDefinitionsResponse(Array.Empty<string>(), request.DefinitionIds!);
}

return await inner.RefreshWorkflowDefinitionsAsync(request, cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using JetBrains.Annotations;
using Medallion.Threading;

namespace Elsa.Workflows.Runtime.Distributed;

/// <summary>
/// Decorator class that adds Distributed Locking to the Workflow Definitions Reloader.
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
/// </summary>
/// <param name="inner"></param>
/// <param name="distributedLockProvider"></param>
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
[UsedImplicitly]
public class WorkflowDefinitionsReloaderDistributedLocking(
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
IWorkflowDefinitionsReloader inner,
IDistributedLockProvider distributedLockProvider) : IWorkflowDefinitionsReloader
{
private const string LockKey = "WorkflowDefinitionsReloader";

/// <summary>
/// This ensures that only one instance of the application can reload workflow definitions at a time, preventing potential conflicts and ensuring consistency across distributed environments.
/// </summary>
/// <param name="cancellationToken"></param>
public async Task ReloadWorkflowDefinitionsAsync(CancellationToken cancellationToken = default)
{
await using var distributedLock = await distributedLockProvider.TryAcquireLockAsync(
LockKey,
TimeSpan.FromMinutes(1),
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
cancellationToken);

if (distributedLock == null)
{
// Another instance is already reloading workflow definitions. Skip this reload.
return;
}

await inner.ReloadWorkflowDefinitionsAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Elsa.Workflows.Management.Contracts;
using Elsa.Workflows.Management.Services;
using Elsa.Workflows.Runtime.ActivationValidators;
using Elsa.Workflows.Runtime.Distributed;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Handlers;
using Elsa.Workflows.Runtime.Options;
Expand Down Expand Up @@ -264,8 +265,15 @@ public override void Apply()
.AddScoped<IActivityExecutionMapper, DefaultActivityExecutionMapper>()
.AddScoped<IWorkflowDefinitionStorePopulator, DefaultWorkflowDefinitionStorePopulator>()
.AddScoped<IRegistriesPopulator, DefaultRegistriesPopulator>()

.AddScoped<IWorkflowDefinitionsRefresher, WorkflowDefinitionsRefresher>()
// Decorating with distributed locking to prevent multiple instances from refreshing the same workflow definitions at the same time.
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
.Decorate<IWorkflowDefinitionsRefresher, WorkflowDefinitionsRefresherDistributedLocking>()

.AddScoped<IWorkflowDefinitionsReloader, WorkflowDefinitionsReloader>()
// Decorating with distributed locking to prevent multiple instances from reloading workflow definitions at the same time.
Comment thread
lukhipolito-nexxbiz marked this conversation as resolved.
Outdated
.Decorate<IWorkflowDefinitionsReloader, WorkflowDefinitionsReloaderDistributedLocking>()

.AddScoped<IWorkflowRegistry, DefaultWorkflowRegistry>()
.AddScoped<IWorkflowMatcher, WorkflowMatcher>()
.AddScoped<IWorkflowInvoker, WorkflowInvoker>()
Expand Down
Loading