Skip to content

Commit 9c6505d

Browse files
committed
Merge remote-tracking branch 'origin/enh/lock-resiliency' into enh/lock-resiliency
2 parents b41a71c + d411967 commit 9c6505d

3 files changed

Lines changed: 21 additions & 12 deletions

File tree

src/modules/Elsa.Resilience.Core/Services/DefaultTransientExceptionStrategy.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,13 @@ public bool IsTransient(Exception exception)
5656
return true;
5757

5858
// Check if the exception message contains any transient patterns
59-
if (!string.IsNullOrEmpty(exception.Message))
60-
{
61-
foreach (var pattern in TransientExceptionMessagePatterns)
62-
{
63-
if (exception.Message.Contains(pattern, StringComparison.OrdinalIgnoreCase))
64-
return true;
65-
}
66-
}
59+
var message = exception.Message;
60+
61+
if (!string.IsNullOrEmpty(message) &&
62+
TransientExceptionMessagePatterns
63+
.Where(pattern => message.Contains(pattern, StringComparison.OrdinalIgnoreCase))
64+
.Any())
65+
return true;
6766

6867
return false;
6968
}

src/modules/Elsa.Workflows.Runtime.Distributed/Services/DistributedWorkflowClient.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ public class DistributedWorkflowClient(
2020
: IWorkflowClient
2121
{
2222
private readonly LocalWorkflowClient _localWorkflowClient = ActivatorUtilities.CreateInstance<LocalWorkflowClient>(serviceProvider, workflowInstanceId);
23-
private readonly Lazy<ResiliencePipeline> _retryPipeline = new(() => CreateRetryPipeline(transientExceptionDetector, logger, workflowInstanceId));
23+
private readonly ITransientExceptionDetector _transientExceptionDetector = transientExceptionDetector;
24+
private readonly ILogger<DistributedWorkflowClient> _logger = logger;
25+
private readonly Lazy<ResiliencePipeline> _retryPipeline = new(CreateRetryPipelineForInstance);
2426

27+
private ResiliencePipeline CreateRetryPipelineForInstance() =>
28+
CreateRetryPipeline(_transientExceptionDetector, _logger, WorkflowInstanceId);
2529
public string WorkflowInstanceId => workflowInstanceId;
2630

2731
public async Task<CreateWorkflowInstanceResponse> CreateInstanceAsync(CreateWorkflowInstanceRequest request, CancellationToken cancellationToken = default)
@@ -133,17 +137,19 @@ private static ResiliencePipeline CreateRetryPipeline(
133137
ILogger<DistributedWorkflowClient> logger,
134138
string workflowInstanceId)
135139
{
140+
const int maxRetryAttempts = 3;
141+
136142
return new ResiliencePipelineBuilder()
137143
.AddRetry(new()
138144
{
139-
MaxRetryAttempts = 3,
145+
MaxRetryAttempts = maxRetryAttempts,
140146
Delay = TimeSpan.FromMilliseconds(500),
141147
BackoffType = DelayBackoffType.Exponential,
142148
UseJitter = true,
143-
ShouldHandle = new PredicateBuilder().Handle<Exception>(transientExceptionDetector.IsTransient),
149+
ShouldHandle = new PredicateBuilder().Handle<Exception>(ex => transientExceptionDetectionService.IsTransient(ex)),
144150
OnRetry = args =>
145151
{
146-
logger.LogWarning(args.Outcome.Exception, "Transient error acquiring lock for workflow instance {WorkflowInstanceId}. Attempt {AttemptNumber} of {MaxAttempts}.", workflowInstanceId, args.AttemptNumber + 1, 3);
152+
logger.LogWarning(args.Outcome.Exception, "Transient error acquiring lock for workflow instance {WorkflowInstanceId}. Attempt {AttemptNumber} of {MaxAttempts}.", workflowInstanceId, args.AttemptNumber + 1, maxRetryAttempts);
147153
return ValueTask.CompletedTask;
148154
}
149155
})

test/component/Elsa.Workflows.ComponentTests/Scenarios/DistributedLockResilience/DistributedLockResilienceTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ private static ResiliencePipeline CreateRetryPipeline(ITransientExceptionDetecto
8181
.AddRetry(new()
8282
{
8383
MaxRetryAttempts = MaxRetryAttempts,
84+
// NOTE: The test retry policy intentionally differs from the production configuration.
85+
// - We use a short, constant delay (10ms) to keep tests fast.
86+
// - We disable jitter and exponential backoff to make timing deterministic and assertions stable.
87+
// The production pipeline uses a larger delay with exponential backoff and jitter for robustness.
8488
Delay = TimeSpan.FromMilliseconds(10),
8589
BackoffType = DelayBackoffType.Constant,
8690
UseJitter = false,

0 commit comments

Comments
 (0)