configure Redis cache and distributed locks

This commit is contained in:
Behzad Alizadeh
2026-10-10 16:51:15 +03:30
parent f4dba6874c
commit 18e91d0b18
9 changed files with 2465 additions and 64 deletions
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>Caspian.WorkflowFramework</PackageId>
<Version>1.1.0-preview.25</Version>
<Version>1.1.0-preview.28</Version>
<Authors>Behzad Alizadeh</Authors>
<Company>Caspian</Company>
<Product>Caspian.WorkflowFramework</Product>
@@ -23,55 +23,59 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Caspian.Elsa.WorkflowContexts" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Workflows.Runtime.Distributed" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Alterations" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Alterations.MassTransit" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Caching.Distributed.MassTransit" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Caching.Distributed.ProtoActor" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.CSharp" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Dapper" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Dapper.Migrations" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.DropIns" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Email" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Elasticsearch" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Environments" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.MySql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.Oracle" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.PostgreSql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.Sqlite" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.SqlServer" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.FileStorage" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Hangfire" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Http" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Identity" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Kafka" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Labels" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Liquid" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.MassTransit" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.MassTransit.AzureServiceBus" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.MassTransit.RabbitMq" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.MongoDb" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.OpenTelemetry" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Quartz" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Quartz.EntityFrameworkCore.MySql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Quartz.EntityFrameworkCore.PostgreSql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Quartz.EntityFrameworkCore.Sqlite" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Retention" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Secrets.Api" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Secrets.Persistence.EntityFrameworkCore.PostgreSql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Secrets.Persistence.EntityFrameworkCore.Sqlite" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Secrets.Persistence.EntityFrameworkCore.SqlServer" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Secrets.Scripting" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Sql.MySql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Sql.PostgreSql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Sql.Sqlite" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Sql.SqlServer" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Sql" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Tenants.AspNetCore" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Webhooks" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa" Version="1.1.0-preview.8" />
<PackageReference Include="Caspian.Elsa.Caching.Redis" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Workflows.Management" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Workflows.Runtime" Version="1.1.0-preview.11" />
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
<PackageReference Include="Caspian.Elsa.WorkflowContexts" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Workflows.Runtime.Distributed" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Alterations" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Alterations.MassTransit" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Caching.Distributed.MassTransit" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Caching.Distributed.ProtoActor" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.CSharp" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Dapper" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Dapper.Migrations" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.DropIns" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Email" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Elasticsearch" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Environments" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.MySql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.Oracle" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.PostgreSql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.Sqlite" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.EntityFrameworkCore.SqlServer" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.FileStorage" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Hangfire" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Http" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Identity" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Kafka" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Labels" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Liquid" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.MassTransit" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.MassTransit.AzureServiceBus" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.MassTransit.RabbitMq" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.MongoDb" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.OpenTelemetry" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Quartz" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Quartz.EntityFrameworkCore.MySql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Quartz.EntityFrameworkCore.PostgreSql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Quartz.EntityFrameworkCore.Sqlite" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Retention" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Secrets.Api" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Secrets.Persistence.EntityFrameworkCore.PostgreSql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Secrets.Persistence.EntityFrameworkCore.Sqlite" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Secrets.Persistence.EntityFrameworkCore.SqlServer" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Secrets.Scripting" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Sql.MySql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Sql.PostgreSql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Sql.Sqlite" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Sql.SqlServer" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Sql" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Tenants.AspNetCore" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa.Webhooks" Version="1.1.0-preview.11" />
<PackageReference Include="Caspian.Elsa" Version="1.1.0-preview.11" />
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
@@ -1,6 +1,9 @@
using Caspian.WorkflowFramework.Contracts;
using Caspian.WorkflowFramework.Contracts;
using Caspian.WorkflowFramework.Services;
using Elsa.Caching.Options;
using Elsa.Caching.Features;
using Microsoft.Extensions.Configuration;
using Caspian.WorkflowFramework.Config;
using Elsa.Common.RecurringTasks;
using Elsa.Common.Serialization;
using Elsa.EntityFrameworkCore.Extensions;
@@ -36,6 +39,10 @@ namespace Caspian.WorkflowFramework;
public static class DependencyInjection
{
public static void AddWorkflowFramework(this IHostApplicationBuilder builder, string sqlServerConnectionString)
=> builder.AddWorkflowFramework(sqlServerConnectionString, null);
public static void AddWorkflowFramework(this IHostApplicationBuilder builder, string sqlServerConnectionString,
Action<CacheFeature>? configureCache)
{
//var configuration = new ConfigurationBuilder()
// .AddJsonFile(appsettingName, optional: false, reloadOnChange: true)
@@ -45,7 +52,7 @@ public static class DependencyInjection
var sqlDatabaseProvider = Enum.Parse<SqlDatabaseProvider>("SqlServer");
const bool useDbContextPooling = false;
const bool runEFCoreMigrations = true;
const bool useCaching = true;
const bool useReadOnlyMode = false;
const WorkflowRuntime workflowRuntime = WorkflowRuntime.Distributed;
const bool disableVariableWrappers = false;
@@ -63,6 +70,13 @@ public static class DependencyInjection
builder.Services.AddElsa(elsa =>
{
elsa
.UseCaching(cache =>
{
cache.Duration = TimeSpan.FromDays(1);
cache.RegisterRedis();
cache.UseConfiguration(builder.Configuration.GetSection("Workflow:Cache"));
configureCache?.Invoke(cache);
})
.UseWorkflows(workflows =>
{
workflows.WithDefaultWorkflowExecutionPipeline(pipeline => pipeline.UseWorkflowExecutionTracing());
@@ -81,8 +95,7 @@ public static class DependencyInjection
ef.UseSqlServer(sqlServerConnectionString);
});
if (useCaching)
management.UseCache();
management.UseCache();
management.SetDefaultLogPersistenceMode(LogPersistenceMode.Inherit);
management.UseReadOnlyMode(useReadOnlyMode);
@@ -106,8 +119,8 @@ public static class DependencyInjection
runtime.UseDistributedRuntime();
}
if (useCaching)
runtime.UseCache();
runtime.UseCache();
runtime.UseConfiguredDistributedLocking(builder.Configuration);
})
.UseCSharp(options =>
{
@@ -128,7 +141,7 @@ public static class DependencyInjection
//});
})
// برای ثبت Activityهایی مانند Elsa.SendHttpRequest
.UseHttp()
.UseHttp(http => http.UseCache())
.UseWorkflowContexts();
});
@@ -143,7 +156,7 @@ public static class DependencyInjection
builder.Services.Configure<RuntimeOptions>(options => { options.InactivityThreshold = TimeSpan.FromSeconds(15); });
builder.Services.Configure<BookmarkQueuePurgeOptions>(options => options.Ttl = TimeSpan.FromSeconds(10));
builder.Services.Configure<CachingOptions>(options => options.CacheDuration = TimeSpan.FromDays(1));
builder.Services.Configure<IncidentOptions>(options => options.DefaultIncidentStrategy = typeof(ContinueWithIncidentsStrategy));
//builder.Services.AddHealthChecks();
@@ -0,0 +1,8 @@
using Medallion.Threading;
namespace Caspian.WorkflowFramework.Config;
internal class PrefixedDistributedLockProvider(IDistributedLockProvider provider, string prefix) : IDistributedLockProvider
{
public IDistributedLock CreateLock(string name) => provider.CreateLock(prefix + name);
}
@@ -0,0 +1,44 @@
using Elsa.Workflows.Runtime.Features;
using Medallion.Threading.Redis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
namespace Caspian.WorkflowFramework.Config;
internal static class WorkflowRuntimeFeatureExtensions
{
internal static void UseConfiguredDistributedLocking(this WorkflowRuntimeFeature runtime, IConfiguration configuration)
{
var legacySettings = configuration.GetSection("Runtime:DistributedLocking");
var settings = configuration.GetSection("Workflow:Runtime:DistributedLocking");
runtime.DistributedLockingOptions = options =>
{
legacySettings.Bind(options);
settings.Bind(options);
};
var providerName = settings["Provider"] ?? legacySettings["Provider"];
switch (providerName?.ToUpperInvariant())
{
case "REDIS":
{
var database = settings.GetValue<int?>("Database")
?? legacySettings.GetValue<int?>("Database")
?? configuration.GetValue<int?>("Redis:Database") ?? -1;
var prefix = settings["KeyPrefix"] ?? legacySettings["KeyPrefix"] ?? "nextbank:workflow:locks:";
if (database < -1) throw new InvalidOperationException("Workflow lock database must be -1 or a non-negative number.");
if (string.IsNullOrWhiteSpace(prefix)) throw new InvalidOperationException("Workflow lock KeyPrefix must not be empty.");
runtime.DistributedLockProvider = sp => new PrefixedDistributedLockProvider(
new RedisDistributedSynchronizationProvider(sp.GetRequiredService<IConnectionMultiplexer>().GetDatabase(database)), prefix);
break;
}
case null:
case "":
case "FILE":
break;
default:
throw new InvalidOperationException($"Unsupported workflow lock provider '{providerName}'. Configure File or Redis.");
}
}
}