# Conflicts:
#	Elsa.sln
This commit is contained in:
Sipke Schoorstra 2022-08-25 19:59:53 +02:00
commit 8e9bb60d63
18 changed files with 341 additions and 65 deletions

View file

@ -122,6 +122,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Jobs", "src\modules\El
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Common", "src\modules\Elsa.Common\Elsa.Common.csproj", "{D229105F-6879-4452-9189-75DE060C0F4C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ProtoActor.Kubernetes", "src\modules\Elsa.ProtoActor.Kubernetes\Elsa.ProtoActor.Kubernetes.csproj", "{26087ED2-9249-4FF3-8869-C11C405E441A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ProtoActor.Common", "src\modules\Elsa.ProtoActor.Common\Elsa.ProtoActor.Common.csproj", "{F599E891-598E-402F-A9B9-95D0B1C661F6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Console3", "src\samples\console\Elsa.Samples.Console3\Elsa.Samples.Console3.csproj", "{517B703A-7653-4036-AAB5-7B4293D39D0F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "worker", "worker", "{389D40B8-005F-46A1-9493-1FE6065F04FD}"
@ -330,14 +334,6 @@ Global
{D229105F-6879-4452-9189-75DE060C0F4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D229105F-6879-4452-9189-75DE060C0F4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D229105F-6879-4452-9189-75DE060C0F4C}.Release|Any CPU.Build.0 = Release|Any CPU
{517B703A-7653-4036-AAB5-7B4293D39D0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{517B703A-7653-4036-AAB5-7B4293D39D0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{517B703A-7653-4036-AAB5-7B4293D39D0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{517B703A-7653-4036-AAB5-7B4293D39D0F}.Release|Any CPU.Build.0 = Release|Any CPU
{197223FB-2472-442C-BD10-2E1F931285BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{197223FB-2472-442C-BD10-2E1F931285BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{197223FB-2472-442C-BD10-2E1F931285BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{197223FB-2472-442C-BD10-2E1F931285BC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{155227F0-A33B-40AA-A4B4-06F813EB921B} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F}
@ -396,7 +392,5 @@ Global
{CF1938A0-BD10-4AA1-9ABC-6D02303A9D53} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{D229105F-6879-4452-9189-75DE060C0F4C} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
{517B703A-7653-4036-AAB5-7B4293D39D0F} = {873BFC3E-63C2-4495-A503-5EC05DCD84E4}
{389D40B8-005F-46A1-9493-1FE6065F04FD} = {155227F0-A33B-40AA-A4B4-06F813EB921B}
{197223FB-2472-442C-BD10-2E1F931285BC} = {389D40B8-005F-46A1-9493-1FE6065F04FD}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,26 @@
using Elsa.ProtoActor.Common.Options;
using Proto;
namespace Elsa.ProtoActor.Common;
public static class DependencyInjectionExtensions
{
public static ActorSystemConfig WithDeveloperLogging(this ActorSystemConfig actorSystemConfig, Action<DeveloperLoggingOptions>? developerLoggingOption = null)
{
var options = new DeveloperLoggingOptions();
developerLoggingOption?.Invoke(options);
return actorSystemConfig.WithDeveloperSupervisionLogging(true)
.WithDeveloperReceiveLogging(options.ReceiveLoggingTimeSpan)
.WithDeadLetterThrottleCount(options.DeadLetterThrottleCount)
.WithDeadLetterThrottleInterval(options.DeadLetterThrottleInterval)
.WithDeadLetterRequestLogging(true);
}
public static ActorSystemConfig ConfigureActorSystemConfig(this ActorSystemConfig systemConfig,
Action<ActorSystemConfig> callback)
{
callback(systemConfig);
return systemConfig;
}
}

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup Label="ProtoActor">
<PackageReference Include="Proto.Actor" Version="0.29.1-alpha.0.6" />
<PackageReference Include="Proto.Cluster" Version="0.29.1-alpha.0.6" />
<PackageReference Include="Proto.Cluster.CodeGen" Version="0.29.1-alpha.0.6" />
<PackageReference Include="Proto.Cluster.Consul" Version="0.29.1-alpha.0.6" />
<PackageReference Include="Proto.Remote" Version="0.29.1-alpha.0.6" />
<PackageReference Include="Proto.Remote.GrpcCore" Version="0.27.1-alpha.0.45" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,9 @@
namespace Elsa.ProtoActor.Common.Options;
public class ClusterConfigurationSettings
{
public TimeSpan HeartBeatExpiration { get; set; } = TimeSpan.FromDays(1);
public TimeSpan ActorRequestTimeout { get; set; } = TimeSpan.FromHours(1);
public TimeSpan ActorActivationTimeout { get; set; } = TimeSpan.FromHours(1);
public TimeSpan ActorSpawnTimeout { get; set; } = TimeSpan.FromHours(1);
}

View file

@ -0,0 +1,8 @@
namespace Elsa.ProtoActor.Common.Options;
public class DeveloperLoggingOptions
{
public TimeSpan ReceiveLoggingTimeSpan { get; set; } = TimeSpan.FromHours(1);
public int DeadLetterThrottleCount { get; set; } = 3;
public TimeSpan DeadLetterThrottleInterval { get; set; } = TimeSpan.FromSeconds(10000);
}

View file

@ -0,0 +1,10 @@
namespace Elsa.ProtoActor.Common.Options;
public class ProviderOptions
{
public string Name { get; set; } = "elsa-cluster";
public bool WithDeveloperLogging { get; set; } = true;
public bool WithMetrics { get; set; }
}

View file

@ -0,0 +1,54 @@
using Elsa.ProtoActor.Common.Options;
using Proto;
using Proto.Cluster;
using Proto.Cluster.Identity;
using Proto.Remote.GrpcNet;
namespace Elsa.ProtoActor.Common;
public class ProtoActorBuilder
{
ProtoActorSystem protoActorSystem = new ();
public ProtoActorBuilder WithClusterProvider(IClusterProvider clusterProvider)
{
protoActorSystem.ClusterProvider = clusterProvider;
return this;
}
public ProtoActorBuilder WithRemoteConfig(GrpcNetRemoteConfig remoteConfig)
{
protoActorSystem.RemoteConfig = remoteConfig;
return this;
}
public ProtoActorSystem Build() => protoActorSystem;
public ProtoActorBuilder WithOptions()
{
return this;
}
public ProtoActorBuilder WithIdentity(IIdentityLookup identityLookup)
{
protoActorSystem.IdentityLookup = identityLookup;
return this;
}
public ProtoActorBuilder WithClusterName(string name)
{
protoActorSystem.Name = name;
return this;
}
public ProtoActorBuilder WithClusterConfiguration(ClusterConfigurationSettings settings)
{
protoActorSystem.ClusterConfigurationSettings = settings;
return this;
}
public ProtoActorBuilder WithActorSystemConfig(ActorSystemConfig actorSystemConfig)
{
protoActorSystem.ActorSystemConfig = actorSystemConfig;
return this;
}
}

View file

@ -0,0 +1,33 @@
using Elsa.ProtoActor.Common.Options;
using Proto;
using Proto.Cluster;
using Proto.Cluster.Identity;
using Proto.Remote.GrpcNet;
namespace Elsa.ProtoActor.Common;
public class ProtoActorSystem
{
public IClusterProvider ClusterProvider { get; set; }
public GrpcNetRemoteConfig RemoteConfig { get; set; }
public ActorSystemConfig ActorSystemConfig { get; set; } = ActorSystemConfig.Setup();
public IIdentityLookup IdentityLookup { get; set; }
public ClusterConfigurationSettings ClusterConfigurationSettings { get; set; } = new();
public string Name { get; set; }
public ProtoActorSystem(IClusterProvider clusterProvider, GrpcNetRemoteConfig remoteConfig, ActorSystemConfig actorSystemConfig, IIdentityLookup identityLookup, string name, ClusterConfigurationSettings clusterConfigurationSettings)
{
ClusterProvider = clusterProvider;
RemoteConfig = remoteConfig;
ActorSystemConfig = actorSystemConfig;
IdentityLookup = identityLookup;
Name = name;
ClusterConfigurationSettings = clusterConfigurationSettings;
}
public ProtoActorSystem()
{
}
}

View file

@ -0,0 +1,18 @@
using System;
using Elsa.ProtoActor.Common;
using Elsa.ProtoActor.Configuration;
namespace Elsa.ProtoActor.Kubernetes;
public static class DependencyInjectionExtensions
{
public static ProtoActorFeature WithKubernetesProvider(this ProtoActorFeature protoActorFeature, Action<KubernetesProviderOptions> providerOptions)
{
var options = new KubernetesProviderOptions();
providerOptions?.Invoke(options);
protoActorFeature.ConfigureProtoActorBuilder(sp =>
new ProtoActorBuilder().UseKubernetesProvider(options).Build());
return protoActorFeature;
}
}

View file

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
</ItemGroup>
<ItemGroup Label="ProtoActor">
<PackageReference Include="Proto.Cluster.Kubernetes" Version="0.29.1-alpha.0.6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Elsa.ProtoActor\Elsa.ProtoActor.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,12 @@
using Elsa.ProtoActor.Common.Options;
using Proto.Cluster.Identity;
using Proto.Cluster.Partition;
namespace Elsa.ProtoActor.Kubernetes;
public class KubernetesProviderOptions : ProviderOptions
{
public string HostAddress { get; set; }
public IIdentityLookup IdentityLookup { get; set; } = new PartitionIdentityLookup();
}

View file

@ -0,0 +1,49 @@
using System;
using Elsa.ProtoActor.Common;
using Microsoft.Extensions.Logging;
using Proto;
using Proto.Cluster;
using Proto.Cluster.Kubernetes;
using Proto.Cluster.Partition;
using Proto.Remote;
using Proto.Remote.GrpcNet;
namespace Elsa.ProtoActor.Kubernetes;
public static class ProtoActorBuilderExtensions
{
public static ProtoActorBuilder UseKubernetesProvider(this ProtoActorBuilder builder, KubernetesProviderOptions options)
{
var (remoteConfig, clusterProvider) = ConfigureForKubernetes(options.HostAddress);
var actorSystemConfig = ActorSystemConfig.Setup();
if (options.WithDeveloperLogging)
{
actorSystemConfig.WithDeveloperLogging();
}
if (options.WithMetrics)
{
actorSystemConfig.WithMetrics();
}
builder.WithClusterProvider(clusterProvider)
.WithRemoteConfig(remoteConfig)
.WithClusterName(options.Name)
.WithIdentity(options.IdentityLookup)
.WithActorSystemConfig(actorSystemConfig);
return builder;
}
private static (GrpcNetRemoteConfig, IClusterProvider) ConfigureForKubernetes(string host)
{
var clusterProvider = new KubernetesProvider();
var remoteConfig = GrpcNetRemoteConfig
.BindToAllInterfaces(advertisedHost: host)
.WithLogLevelForDeserializationErrors(LogLevel.Critical)
.WithRemoteDiagnostics(true);
return (remoteConfig, clusterProvider);
}
}

View file

@ -2,17 +2,18 @@ using System;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.ProtoActor.Common;
using Elsa.ProtoActor.Extensions;
using Elsa.ProtoActor.Grains;
using Elsa.ProtoActor.HostedServices;
using Elsa.ProtoActor.Implementations;
using Elsa.Runtime.Protos;
using Elsa.Workflows.Runtime.Features;
using Google.Protobuf.WellKnownTypes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Proto;
using Proto.Cluster;
using Proto.Cluster.Partition;
using Proto.Cluster.Testing;
using Proto.DependencyInjection;
using Proto.Remote;
using Proto.Remote.GrpcNet;
@ -29,23 +30,50 @@ public class ProtoActorFeature : FeatureBase
public override void Configure()
{
// Configure runtime with ProtoActor workflow invoker.
Module.Configure<WorkflowRuntimeFeature>().WorkflowInvokerFactory = sp => ActivatorUtilities.CreateInstance<ProtoActorWorkflowInvoker>(sp);
Module.Configure<WorkflowRuntimeFeature>().WorkflowInvokerFactory =
sp => ActivatorUtilities.CreateInstance<ProtoActorWorkflowInvoker>(sp);
}
public ProtoActorFeature ConfigureProtoActorBuilder(Func<IServiceProvider, ProtoActorSystem> factory)
{
ProtoActorBuilderFactory = factory;
return this;
}
//configure the default one
public Func<IServiceProvider, ProtoActorSystem> ProtoActorBuilderFactory { get; set; } =
_ => new ProtoActorBuilder().UseLocalhostProvider("elsa-cluster", true) .Build();
public override void Apply()
{
var services = Services;
var systemConfig = GetSystemConfig();
services.AddSingleton(ProtoActorBuilderFactory);
// Logging.
Log.SetLoggerFactory(LoggerFactory.Create(l => l.AddConsole().SetMinimumLevel(LogLevel.Warning)));
// Actor System.
services.AddSingleton(sp =>
{
var system = new ActorSystem(systemConfig).WithServiceProvider(sp);
var remoteConfig = GetRemoteConfig();
var clusterConfig = GetClusterConfig(system, "my-cluster");
var protoActorSystem = sp.GetService<ProtoActorSystem>();
var system = new ActorSystem(protoActorSystem!.ActorSystemConfig).WithServiceProvider(sp);
var remoteConfig = protoActorSystem.RemoteConfig
.WithProtoMessages(MessagesReflection.Descriptor)
.WithProtoMessages(EmptyReflection.Descriptor);
var workflowDefinitionProps = system.DI().PropsFor<WorkflowDefinitionGrainActor>();
var workflowInstanceProps = system.DI().PropsFor<WorkflowInstanceGrainActor>();
var clusterConfig =
ClusterConfig
.Setup(protoActorSystem.Name, protoActorSystem.ClusterProvider, protoActorSystem.IdentityLookup)
.WithHeartbeatExpiration(protoActorSystem.ClusterConfigurationSettings.HeartBeatExpiration)
.WithActorRequestTimeout(protoActorSystem.ClusterConfigurationSettings.ActorRequestTimeout)
.WithActorActivationTimeout(protoActorSystem.ClusterConfigurationSettings.ActorActivationTimeout)
.WithActorSpawnTimeout(protoActorSystem.ClusterConfigurationSettings.ActorSpawnTimeout)
.WithClusterKind(WorkflowDefinitionGrainActor.Kind, workflowDefinitionProps)
.WithClusterKind(WorkflowInstanceGrainActor.Kind, workflowInstanceProps);
system
.WithRemote(remoteConfig)
@ -59,8 +87,10 @@ public class ProtoActorFeature : FeatureBase
// Actors.
services
.AddSingleton(sp => new WorkflowDefinitionGrainActor((context, _) => ActivatorUtilities.CreateInstance<WorkflowDefinitionGrain>(sp, context)))
.AddSingleton(sp => new WorkflowInstanceGrainActor((context, _) => ActivatorUtilities.CreateInstance<WorkflowInstanceGrain>(sp, context)));
.AddSingleton(sp => new WorkflowDefinitionGrainActor((context, _) =>
ActivatorUtilities.CreateInstance<WorkflowDefinitionGrain>(sp, context)))
.AddSingleton(sp => new WorkflowInstanceGrainActor((context, _) =>
ActivatorUtilities.CreateInstance<WorkflowInstanceGrain>(sp, context)));
// Client factory.
services.AddSingleton<GrainClientFactory>();
@ -70,46 +100,4 @@ public class ProtoActorFeature : FeatureBase
{
Services.AddHostedService<WorkflowServerHost>();
}
private static ActorSystemConfig GetSystemConfig() =>
ActorSystemConfig
.Setup()
.WithDeveloperSupervisionLogging(true)
.WithDeveloperReceiveLogging(TimeSpan.FromHours(1))
.WithDeadLetterThrottleCount(3)
.WithDeadLetterThrottleInterval(TimeSpan.FromSeconds(10000))
.WithDeveloperSupervisionLogging(true)
.WithDeadLetterRequestLogging(true);
private static GrpcNetRemoteConfig GetRemoteConfig() => Proto.Remote.GrpcNet.GrpcNetRemoteConfig
.BindToLocalhost()
.WithProtoMessages(MessagesReflection.Descriptor);
private static ClusterConfig GetClusterConfig(ActorSystem system, string clusterName)
{
//var clusterProvider = new ConsulProvider(new ConsulProviderConfig{});
var clusterProvider = new TestProvider(new TestProviderOptions(), new InMemAgent());
var workflowDefinitionProps = system.DI().PropsFor<WorkflowDefinitionGrainActor>();
var workflowInstanceProps = system.DI().PropsFor<WorkflowInstanceGrainActor>();
var clusterConfig =
ClusterConfig
// .Setup("MyCluster", clusterProvider, new IdentityStorageLookup(GetIdentityLookup(clusterName)))
.Setup(clusterName, clusterProvider, new PartitionIdentityLookup())
.WithHeartbeatExpiration(TimeSpan.FromDays(1))
//.WithTimeout(TimeSpan.FromHours(1))
.WithActorRequestTimeout(TimeSpan.FromHours(1))
.WithActorActivationTimeout(TimeSpan.FromHours(1))
.WithActorSpawnTimeout(TimeSpan.FromHours(1))
.WithClusterKind(WorkflowDefinitionGrainActor.Kind, workflowDefinitionProps)
.WithClusterKind(WorkflowInstanceGrainActor.Kind, workflowInstanceProps)
;
return clusterConfig;
}
// private static IIdentityStorage GetIdentityLookup(string clusterName) =>
// new RedisIdentityStorage(clusterName, ConnectionMultiplexer
// .Connect("localhost:6379" /* use proper config */)
// );
}

View file

@ -27,6 +27,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Elsa.ProtoActor.Common\Elsa.ProtoActor.Common.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Persistence\Elsa.Workflows.Persistence.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />

View file

@ -1,4 +1,6 @@
using System;
using Elsa.ProtoActor.Common;
using Elsa.ProtoActor.Common.Options;
using Elsa.ProtoActor.Configuration;
using Elsa.Workflows.Runtime.Features;
@ -11,4 +13,14 @@ public static class DependencyInjectionExtensions
feature.Module.Configure(configure);
return feature;
}
public static ProtoActorFeature WithLocalhostProvider(this ProtoActorFeature protoActorFeature, Action<ProviderOptions>? providerOptions = null)
{
var options = new ProviderOptions();
providerOptions?.Invoke(options);
protoActorFeature.ConfigureProtoActorBuilder(sp =>
new ProtoActorBuilder().UseLocalhostProvider(options.Name, options.WithDeveloperLogging).Build());
return protoActorFeature;
}
}

View file

@ -0,0 +1,23 @@
using Elsa.ProtoActor.Common;
using Proto;
using Proto.Cluster.Partition;
using Proto.Cluster.Testing;
using Proto.Remote.GrpcNet;
namespace Elsa.ProtoActor.Extensions;
public static class ProtoActorBuilderExtensions
{
public static ProtoActorBuilder UseLocalhostProvider(this ProtoActorBuilder builder, string clusterName, bool withDeveloperLogging)
{
var actorSystemConfig = ActorSystemConfig.Setup();
builder.WithClusterProvider(new TestProvider(new TestProviderOptions(), new InMemAgent()))
.WithRemoteConfig(GrpcNetRemoteConfig
.BindToLocalhost())
.WithClusterName(clusterName)
.WithIdentity(new PartitionIdentityLookup())
.WithActorSystemConfig(withDeveloperLogging ? actorSystemConfig.WithDeveloperLogging() : actorSystemConfig);
return builder;
}
}

View file

@ -16,7 +16,7 @@ public static class ProtoInputExtensions
foreach (var (key, value) in input)
{
data[key] = new Json
data[key] = new Runtime.Protos.Json
{
Text = JsonSerializer.Serialize(value)
};

View file

@ -25,7 +25,7 @@ var services = builder.Services;
services
.AddElsa(elsa => elsa
.UseWorkflows()
.UseRuntime(runtime => runtime.UseProtoActor())
.UseRuntime(runtime => runtime.UseProtoActor(f=> f.WithLocalhostProvider(opt => opt.Name = "my-cluster")))
.UseManagement(management => management
.AddActivity<Sequence>()
.AddActivity<WriteLine>()