Catch exceptions and log instead of crashing (#6477)

* Catch exceptions and log instead of crashing

Modified both WorkflowInstance and WorkflowDefinition state loading to prevent throwing exceptions if the state fails to load successfully.

Also modified the CleanupJob to catch exceptions thrown by cleaning up and logging as errors and continuing instead of crashing.

Fixes 6473

* Use LogError instead of LogWarning for deserialization failure.

Updated the log level to LogError when workflow definition state deserialization fails. This change ensures better visibility and prioritization of critical issues during state handling.

---------

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
This commit is contained in:
Matthew Vance 2025-03-18 11:27:04 -07:00 committed by GitHub
parent 8e9d5e356e
commit aa989ce13d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 9 deletions

View file

@ -13,12 +13,13 @@ using Elsa.Common.Entities;
using Elsa.Workflows;
using Elsa.Workflows.Management;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Elsa.EntityFrameworkCore.Modules.Management;
/// <inheritdoc />
[UsedImplicitly]
public class EFCoreWorkflowDefinitionStore(EntityStore<ManagementElsaDbContext, WorkflowDefinition> store, IPayloadSerializer payloadSerializer)
public class EFCoreWorkflowDefinitionStore(EntityStore<ManagementElsaDbContext, WorkflowDefinition> store, IPayloadSerializer payloadSerializer, ILogger<EFCoreWorkflowDefinitionStore> logger)
: IWorkflowDefinitionStore
{
/// <inheritdoc />
@ -178,8 +179,15 @@ public class EFCoreWorkflowDefinitionStore(EntityStore<ManagementElsaDbContext,
var data = new WorkflowDefinitionState(entity.Options, entity.Variables, entity.Inputs, entity.Outputs, entity.Outcomes, entity.CustomProperties);
var json = (string?)managementElsaDbContext.Entry(entity).Property("Data").CurrentValue;
if (!string.IsNullOrWhiteSpace(json))
data = payloadSerializer.Deserialize<WorkflowDefinitionState>(json);
try
{
if (!string.IsNullOrWhiteSpace(json))
data = payloadSerializer.Deserialize<WorkflowDefinitionState>(json);
}
catch (Exception exp)
{
logger.LogError(exp, "Could not deserialize workflow definition state: {DefinitionId}. Reverting to default state", entity.DefinitionId);
}
entity.Options = data.Options;
entity.Variables = data.Variables;

View file

@ -11,6 +11,7 @@ using Elsa.Workflows.Management.Models;
using Elsa.Workflows.Management.Options;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Open.Linq.AsyncExtensions;
@ -26,6 +27,7 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore
private readonly IWorkflowStateSerializer _workflowStateSerializer;
private readonly ICompressionCodecResolver _compressionCodecResolver;
private readonly IOptions<ManagementOptions> _options;
private readonly ILogger<EFCoreWorkflowInstanceStore> _logger;
/// <summary>
/// Constructor.
@ -34,12 +36,14 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore
EntityStore<ManagementElsaDbContext, WorkflowInstance> store,
IWorkflowStateSerializer workflowStateSerializer,
ICompressionCodecResolver compressionCodecResolver,
IOptions<ManagementOptions> options)
IOptions<ManagementOptions> options,
ILogger<EFCoreWorkflowInstanceStore> logger)
{
_store = store;
_workflowStateSerializer = workflowStateSerializer;
_compressionCodecResolver = compressionCodecResolver;
_options = options;
_logger = logger;
}
/// <inheritdoc />
@ -236,12 +240,18 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore
var compressionAlgorithm = (string?)managementElsaDbContext.Entry(entity).Property("DataCompressionAlgorithm").CurrentValue ?? nameof(None);
var compressionStrategy = _compressionCodecResolver.Resolve(compressionAlgorithm);
if (!string.IsNullOrWhiteSpace(json))
try
{
json = await compressionStrategy.DecompressAsync(json, cancellationToken);
data = _workflowStateSerializer.Deserialize(json);
if (!string.IsNullOrWhiteSpace(json))
{
json = await compressionStrategy.DecompressAsync(json, cancellationToken);
data = _workflowStateSerializer.Deserialize(json);
}
}
catch (Exception exp)
{
_logger.LogWarning(exp, "Exception while deserializing workflow instance state: {InstanceId}. Reverting to default state", entity.Id);
}
entity.WorkflowState = data;
}

View file

@ -71,7 +71,14 @@ public class CleanupJob(
await foreach (var entities in collector.GetRelatedEntitiesGeneric(page.Items).WithCancellation(cancellationToken))
{
await cleanupService.Cleanup(entities);
try
{
await cleanupService.Cleanup(entities);
}
catch (Exception exp)
{
_logger.LogError(exp, "Failed to clean up {Type} because exception thrown", collectorService.Key.Name);
}
}
}