Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge scope change into main #4738

Merged
merged 3 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Update service lifetimes and use IServiceScopeFactory for runtime act…
…ivities

This commit introduces several modifications targeting the adjustment of service lifetimes. Classes that were previously instantiated as Scoped are changed to Singletons, and vice versa. Furthermore, it employs IServiceScopeFactory to resolve services required for runtime activities. This improves the application's infrastructure by ensuring more efficient service lifetimes and optimizing the runtime resource allocation.
  • Loading branch information
sfmskywalker committed Jan 2, 2024
commit 62db1d6aff92ba0cb928a98fe76cfa0d8da29841
2 changes: 1 addition & 1 deletion .github/workflows/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
TAG_NAME=${TAG_NAME#refs/tags/} # remove the refs/tags/ prefix
echo "VERSION=${TAG_NAME}" >> $GITHUB_ENV
else
echo "VERSION=3.0.0-preview.${{github.run_number}}" >> $GITHUB_ENV
echo "VERSION=3.1.0-preview.${{github.run_number}}" >> $GITHUB_ENV
fi
- name: Build
run: dotnet build --configuration Release /p:Version=${VERSION}
Expand Down
12 changes: 5 additions & 7 deletions src/modules/Elsa.Dsl/Features/DslFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,16 @@

namespace Elsa.Dsl.Features;

/// <inheritdoc />
[DependsOn(typeof(ExpressionsFeature))]
public class DslFeature : FeatureBase
public class DslFeature(IModule module) : FeatureBase(module)
{
public DslFeature(IModule module) : base(module)
{
}

/// <inheritdoc />
public override void Configure()
{
Services
.AddScoped<IDslEngine, DslEngine>()
.AddSingleton<ITypeSystem, TypeSystem>()
.AddSingleton<IFunctionActivityRegistry, FunctionActivityRegistry>();
.AddScoped<ITypeSystem, TypeSystem>()
.AddScoped<IFunctionActivityRegistry, FunctionActivityRegistry>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static IModule UseExpressions(this IModule module, Action<ExpressionsFeat
/// <param name="services">The services.</param>
public static IServiceCollection AddExpressionDescriptorProvider<T>(this IServiceCollection services) where T: class, IExpressionDescriptorProvider
{
return services.AddScoped<IExpressionDescriptorProvider, T>();
return services.AddSingleton<IExpressionDescriptorProvider, T>();
}

/// <summary>
Expand Down
14 changes: 7 additions & 7 deletions src/modules/Elsa.ProtoActor/Features/ProtoActorFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public override void Apply()
var services = Services;

// Register ActorSystem.
services.AddScoped(sp =>
services.AddSingleton(sp =>
{
var systemConfig = Proto.ActorSystemConfig
.Setup()
Expand Down Expand Up @@ -119,22 +119,22 @@ public override void Apply()
Log.SetLoggerFactory(LoggerFactory.Create(l => l.AddConsole().SetMinimumLevel(LogLevel.Warning)));

// Persistence.
services.AddScoped(PersistenceProvider);
services.AddTransient(PersistenceProvider);

// Mappers.
services
.AddScoped<BookmarkMapper>()
.AddScoped<ExceptionMapper>()
.AddSingleton<ExceptionMapper>()
.AddScoped<WorkflowExecutionResultMapper>()
.AddScoped<ActivityIncidentStateMapper>()
.AddScoped<WorkflowStatusMapper>()
.AddScoped<WorkflowSubStatusMapper>();
.AddSingleton<ActivityIncidentStateMapper>()
.AddSingleton<WorkflowStatusMapper>()
.AddSingleton<WorkflowSubStatusMapper>();

// Mediator handlers.
services.AddHandlersFrom<ProtoActorFeature>();

// Cluster.
services.AddScoped(sp => sp.GetRequiredService<ActorSystem>().Cluster());
services.AddSingleton(sp => sp.GetRequiredService<ActorSystem>().Cluster());

// Actors.
services
Expand Down
107 changes: 53 additions & 54 deletions src/modules/Elsa.ProtoActor/Grains/WorkflowInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Options;
using Elsa.Workflows.State;
using Microsoft.Extensions.DependencyInjection;
using Proto;
using Proto.Cluster;
using Proto.Persistence;
Expand All @@ -21,12 +22,10 @@ namespace Elsa.ProtoActor.Grains;
internal class WorkflowInstance : WorkflowInstanceBase
{
private const int MaxSnapshotsToKeep = 5;
private readonly IWorkflowDefinitionService _workflowDefinitionService;

private readonly IServiceScopeFactory _scopeFactory;
private readonly IWorkflowHostFactory _workflowHostFactory;
private readonly IWorkflowStateSerializer _workflowStateSerializer;
private readonly IWorkflowInstanceManager _workflowInstanceManager;
private readonly WorkflowStateMapper _workflowStateMapper;
private readonly BookmarkMapper _bookmarkMapper;
private readonly WorkflowStatusMapper _workflowStatusMapper;
private readonly WorkflowSubStatusMapper _workflowSubStatusMapper;
private readonly Persistence _persistence;
Expand All @@ -41,26 +40,21 @@ internal class WorkflowInstance : WorkflowInstanceBase

/// <inheritdoc />
public WorkflowInstance(
IWorkflowDefinitionService workflowDefinitionService,
IServiceScopeFactory scopeFactory,
IWorkflowHostFactory workflowHostFactory,
IWorkflowStateSerializer workflowStateSerializer,
IWorkflowInstanceManager workflowInstanceManager,
IProvider provider,
IContext context,
WorkflowStateMapper workflowStateMapper,
BookmarkMapper bookmarkMapper,
WorkflowStatusMapper workflowStatusMapper,
WorkflowSubStatusMapper workflowSubStatusMapper) : base(context)
WorkflowSubStatusMapper workflowSubStatusMapper
) : base(context)
{
_workflowDefinitionService = workflowDefinitionService;
_scopeFactory = scopeFactory;
_workflowHostFactory = workflowHostFactory;
_workflowStateSerializer = workflowStateSerializer;
_workflowInstanceManager = workflowInstanceManager;
_workflowStateMapper = workflowStateMapper;
_bookmarkMapper = bookmarkMapper;
_workflowStatusMapper = workflowStatusMapper;
_workflowSubStatusMapper = workflowSubStatusMapper;
_persistence = Persistence.WithSnapshotting(provider, Context.ClusterIdentity()!.Identity, ApplySnapshot);
_persistence = Persistence.WithSnapshotting(provider, context.ClusterIdentity()!.Identity, ApplySnapshot);
}

/// <inheritdoc />
Expand All @@ -72,28 +66,27 @@ public override async Task OnStarted()
return; // No state yet to recover from.

var cancellationToken = Context.CancellationToken;
using var scope = _scopeFactory.CreateScope();
var workflowDefinitionService = scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionService>();

// Load the workflow definition.
var workflowDefinition = await _workflowDefinitionService.FindAsync(_definitionId, VersionOptions.SpecificVersion(_version), cancellationToken);
var workflowDefinition = await workflowDefinitionService.FindAsync(_definitionId, VersionOptions.SpecificVersion(_version), cancellationToken);

if (workflowDefinition == null)
throw new Exception("Workflow definition is no longer available");

// Materialize the workflow.
var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
var workflow = await workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);

// Create an initial workflow state.
if (_workflowState == null!)
{
_workflowState = new WorkflowState
{
DefinitionId = workflow.Identity.DefinitionId,
DefinitionVersion = workflow.Identity.Version,
};
_workflowState = new WorkflowState { DefinitionId = workflow.Identity.DefinitionId, DefinitionVersion = workflow.Identity.Version, };
}

// Create a workflow host.
_workflowHost = await _workflowHostFactory.CreateAsync(workflow, _workflowState, cancellationToken);
var workflowHostFactory = scope.ServiceProvider.GetRequiredService<IWorkflowHostFactory>();
_workflowHost = await workflowHostFactory.CreateAsync(workflow, _workflowState, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -128,10 +121,7 @@ public override async Task CanStart(StartWorkflowRequest request, Action<CanStar

Context.ReenterAfter(task, async canStart =>
{
respond(new CanStartWorkflowResponse
{
CanStart = await canStart
});
respond(new CanStartWorkflowResponse { CanStart = await canStart });
});
}

Expand Down Expand Up @@ -181,15 +171,19 @@ public override async Task Start(StartWorkflowRequest request, Action<WorkflowEx
await SaveSnapshotAsync();
SaveWorkflowInstance(workflowState);

using var scope = _scopeFactory.CreateScope();
var bookmarkMapper = scope.ServiceProvider.GetRequiredService<BookmarkMapper>();
var mappedBookmarks = bookmarkMapper.Map(workflowState.Bookmarks).ToList();

respond(new WorkflowExecutionResponse
{
Result = result,
Bookmarks = { _bookmarkMapper.Map(workflowState.Bookmarks).ToList() },
Bookmarks = { mappedBookmarks },
Status = _workflowStatusMapper.Map(workflowState.Status),
SubStatus = _workflowSubStatusMapper.Map(workflowState.SubStatus),
TriggeredActivityId = string.Empty,
WorkflowInstanceId = instanceId
});
});
});
}

Expand Down Expand Up @@ -238,26 +232,27 @@ public override async Task Resume(ResumeWorkflowRequest request, Action<Workflow
}

var task = _workflowHost.ResumeWorkflowAsync(resumeWorkflowHostOptions, cancellationToken);

Context.ReenterAfter(task, async () =>
{
var finished = _workflowHost.WorkflowState.Status == Workflows.WorkflowStatus.Finished;

_workflowState = _workflowHost.WorkflowState;

await SaveSnapshotAsync();
SaveWorkflowInstance(_workflowState);
using var scope = _scopeFactory.CreateScope();
await SaveWorkflowInstanceCoreAsync(scope.ServiceProvider, _workflowState);
var bookmarkMapper = scope.ServiceProvider.GetRequiredService<BookmarkMapper>();

var response = new WorkflowExecutionResponse
{
Result = finished ? RunWorkflowResult.Finished : RunWorkflowResult.Suspended,
Bookmarks = { _bookmarkMapper.Map(_workflowHost.WorkflowState.Bookmarks).ToList() },
Bookmarks = { bookmarkMapper.Map(_workflowHost.WorkflowState.Bookmarks).ToList() },
TriggeredActivityId = string.Empty,
WorkflowInstanceId = _workflowState.Id,
Status = _workflowStatusMapper.Map(_workflowState.Status),
SubStatus = _workflowSubStatusMapper.Map(_workflowState.SubStatus)
};
};

respond(response);
});
}
Expand All @@ -268,32 +263,29 @@ public override async Task Resume(ResumeWorkflowRequest request, Action<Workflow
/// <inheritdoc />
public override async Task<ExportWorkflowStateResponse> ExportState(ExportWorkflowStateRequest request)
{
var json = await _workflowStateSerializer.SerializeAsync(_workflowHost.WorkflowState);

var response = new ExportWorkflowStateResponse
{
SerializedWorkflowState = new Json
{
Text = json
}
};
using var scope = _scopeFactory.CreateScope();
var workflowStateSerializer = scope.ServiceProvider.GetRequiredService<IWorkflowStateSerializer>();
var json = await workflowStateSerializer.SerializeAsync(_workflowHost.WorkflowState);
var response = new ExportWorkflowStateResponse { SerializedWorkflowState = new Json { Text = json } };

return response;
}

/// <inheritdoc />
public override async Task<ImportWorkflowStateResponse> ImportState(ImportWorkflowStateRequest request)
{
var workflowState = await _workflowStateSerializer.DeserializeAsync(request.SerializedWorkflowState.Text);
using var scope = _scopeFactory.CreateScope();
var workflowStateSerializer = scope.ServiceProvider.GetRequiredService<IWorkflowStateSerializer>();
var workflowState = await workflowStateSerializer.DeserializeAsync(request.SerializedWorkflowState.Text);

_workflowState = workflowState;
_workflowHost.WorkflowState = workflowState;
_definitionId = workflowState.DefinitionId;
_instanceId = workflowState.Id;
_version = workflowState.DefinitionVersion;
_workflowHost = await CreateWorkflowHostAsync(workflowState, Context.CancellationToken);

SaveWorkflowInstance(workflowState);
await SaveWorkflowInstanceCoreAsync(scope.ServiceProvider, workflowState);
return new ImportWorkflowStateResponse();
}

Expand All @@ -313,25 +305,30 @@ private async Task SaveSnapshotAsync()

private async Task<IWorkflowHost> CreateWorkflowHostAsync(string definitionId, VersionOptions versionOptions, CancellationToken cancellationToken)
{
var workflowDefinition = await _workflowDefinitionService.FindAsync(definitionId, versionOptions, cancellationToken);
using var scope = _scopeFactory.CreateScope();
var workflowDefinitionService = scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionService>();
var workflowDefinition = await workflowDefinitionService.FindAsync(definitionId, versionOptions, cancellationToken);

if (workflowDefinition == null)
throw new Exception("Specified workflow definition and version does not exist");

var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
return await _workflowHostFactory.CreateAsync(workflow, cancellationToken);
var workflow = await workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
var workflowHostFactory = scope.ServiceProvider.GetRequiredService<IWorkflowHostFactory>();
return await workflowHostFactory.CreateAsync(workflow, cancellationToken);
}

private async Task<IWorkflowHost> CreateWorkflowHostAsync(WorkflowState workflowState, CancellationToken cancellationToken)
{
var definitionId = workflowState.DefinitionId;
var versionOptions = VersionOptions.SpecificVersion(workflowState.DefinitionVersion);
var workflowDefinition = await _workflowDefinitionService.FindAsync(definitionId, versionOptions, cancellationToken);
using var scope = _scopeFactory.CreateScope();
var workflowDefinitionService = scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionService>();
var workflowDefinition = await workflowDefinitionService.FindAsync(definitionId, versionOptions, cancellationToken);

if (workflowDefinition == null)
throw new Exception("Specified workflow definition and version does not exist");

var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
var workflow = await workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
return await _workflowHostFactory.CreateAsync(workflow, workflowState, cancellationToken);
}

Expand All @@ -340,13 +337,15 @@ private async Task<IWorkflowHost> CreateWorkflowHostAsync(WorkflowState workflow
/// </summary>
private void SaveWorkflowInstance(WorkflowState workflowState)
{
var saveInstanceTask = SaveWorkflowInstanceCoreAsync(workflowState);
using var scope = _scopeFactory.CreateScope();
var saveInstanceTask = SaveWorkflowInstanceCoreAsync(scope.ServiceProvider, workflowState);
Context.ReenterAfter(saveInstanceTask, () => { });
}

private Task SaveWorkflowInstanceCoreAsync(WorkflowState workflowState)
private Task SaveWorkflowInstanceCoreAsync(IServiceProvider sp, WorkflowState workflowState)
{
var workflowInstance = _workflowStateMapper.Map(workflowState)!;
return _workflowInstanceManager.SaveAsync(workflowInstance);
var workflowInstanceManager = sp.GetRequiredService<IWorkflowInstanceManager>();
return workflowInstanceManager.SaveAsync(workflowInstance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ private void AddElsaCore(IServiceCollection services)
.AddScoped<IActivitySchedulerFactory, ActivitySchedulerFactory>()
.AddScoped<IHasher, Hasher>()
.AddScoped<IBookmarkHasher, BookmarkHasher>()
.AddScoped(IdentityGenerator)
.AddSingleton(IdentityGenerator)
.AddScoped<IBookmarkPayloadSerializer>(sp => ActivatorUtilities.CreateInstance<BookmarkPayloadSerializer>(sp))
.AddSingleton<IActivityDescriber, ActivityDescriber>()
.AddSingleton<IActivityRegistry, ActivityRegistry>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,8 @@
namespace Elsa.Workflows.Services;

/// <inheritdoc />
public class PropertyDefaultValueResolver : IPropertyDefaultValueResolver
public class PropertyDefaultValueResolver(IServiceScopeFactory scopeFactory) : IPropertyDefaultValueResolver
{
private readonly IServiceProvider _serviceProvider;

/// <summary>
/// Initializes a new instance of the <see cref="PropertyDefaultValueResolver"/> class.
/// </summary>
public PropertyDefaultValueResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

/// <inheritdoc />
public object? GetDefaultValue(PropertyInfo activityPropertyInfo)
{
Expand All @@ -30,8 +20,7 @@ public PropertyDefaultValueResolver(IServiceProvider serviceProvider)
return inputAttribute.DefaultValue;

var providerType = inputAttribute.DefaultValueProvider;

using var scope = _serviceProvider.CreateScope();
using var scope = scopeFactory.CreateScope();
var provider = (IActivityPropertyDefaultValueProvider) ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, providerType);
return provider.GetDefaultValue(activityPropertyInfo);
}
Expand Down
Loading