diff --git a/Elsa.sln.DotSettings b/Elsa.sln.DotSettings index 8bb0ccf33..d88a97a28 100644 --- a/Elsa.sln.DotSettings +++ b/Elsa.sln.DotSettings @@ -1,4 +1,4 @@ - + False 1000 1 @@ -17,4 +17,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs index aaef7368d..64b000aa7 100644 --- a/src/bundles/Elsa.Server.Web/Program.cs +++ b/src/bundles/Elsa.Server.Web/Program.cs @@ -110,7 +110,7 @@ services }); if(useZipCompression) - management.SetCompressionAlgorithm(nameof(GZip)); + management.SetCompressionAlgorithm(nameof(Zstd)); }) .UseWorkflowRuntime(runtime => { diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstanceStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstanceStore.cs index b1df465b2..6990b94d6 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstanceStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstanceStore.cs @@ -23,7 +23,7 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore { private readonly EntityStore _store; private readonly IWorkflowStateSerializer _workflowStateSerializer; - private readonly ICompressionStrategyResolver _compressionStrategyResolver; + private readonly ICompressionCodecResolver _compressionCodecResolver; private readonly IOptions _options; /// @@ -32,12 +32,12 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore public EFCoreWorkflowInstanceStore( EntityStore store, IWorkflowStateSerializer workflowStateSerializer, - ICompressionStrategyResolver compressionStrategyResolver, + ICompressionCodecResolver compressionCodecResolver, IOptions options) { _store = store; _workflowStateSerializer = workflowStateSerializer; - _compressionStrategyResolver = compressionStrategyResolver; + _compressionCodecResolver = compressionCodecResolver; _options = options; } @@ -137,8 +137,8 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore var data = entity.WorkflowState; var json = await _workflowStateSerializer.SerializeAsync(data, cancellationToken); var compressionAlgorithm = _options.Value.CompressionAlgorithm ?? nameof(None); - var compressionStrategy = _compressionStrategyResolver.Resolve(compressionAlgorithm); - var compressedJson = await compressionStrategy.CompressAsync(json, cancellationToken); + var compressionCodec = _compressionCodecResolver.Resolve(compressionAlgorithm); + var compressedJson = await compressionCodec.CompressAsync(json, cancellationToken); managementElsaDbContext.Entry(entity).Property("Data").CurrentValue = compressedJson; managementElsaDbContext.Entry(entity).Property("DataCompressionAlgorithm").CurrentValue = compressionAlgorithm; @@ -152,7 +152,7 @@ public class EFCoreWorkflowInstanceStore : IWorkflowInstanceStore var data = entity.WorkflowState; var json = (string?)managementElsaDbContext.Entry(entity).Property("Data").CurrentValue; var compressionAlgorithm = (string?)managementElsaDbContext.Entry(entity).Property("DataCompressionAlgorithm").CurrentValue ?? nameof(None); - var compressionStrategy = _compressionStrategyResolver.Resolve(compressionAlgorithm); + var compressionStrategy = _compressionCodecResolver.Resolve(compressionAlgorithm); if (!string.IsNullOrWhiteSpace(json)) { diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs index d0402efb7..4eb12f62a 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs @@ -25,7 +25,7 @@ public class EFCoreActivityExecutionStore : IActivityExecutionStore private readonly EntityStore _store; private readonly ISafeSerializer _safeSerializer; private readonly IPayloadSerializer _payloadSerializer; - private readonly ICompressionStrategyResolver _compressionStrategyResolver; + private readonly ICompressionCodecResolver _compressionCodecResolver; private readonly IOptions _options; /// @@ -35,13 +35,13 @@ public class EFCoreActivityExecutionStore : IActivityExecutionStore EntityStore store, ISafeSerializer safeSerializer, IPayloadSerializer payloadSerializer, - ICompressionStrategyResolver compressionStrategyResolver, + ICompressionCodecResolver compressionCodecResolver, IOptions options) { _store = store; _safeSerializer = safeSerializer; _payloadSerializer = payloadSerializer; - _compressionStrategyResolver = compressionStrategyResolver; + _compressionCodecResolver = compressionCodecResolver; _options = options; } @@ -75,7 +75,7 @@ public class EFCoreActivityExecutionStore : IActivityExecutionStore { var compressionAlgorithm = _options.Value.CompressionAlgorithm ?? nameof(None); var serializedActivityState = entity.ActivityState != null ? await _safeSerializer.SerializeAsync(entity.ActivityState, cancellationToken) : default; - var compressedSerializedActivityState = serializedActivityState != null ? await _compressionStrategyResolver.Resolve(compressionAlgorithm).CompressAsync(serializedActivityState, cancellationToken) : default; + var compressedSerializedActivityState = serializedActivityState != null ? await _compressionCodecResolver.Resolve(compressionAlgorithm).CompressAsync(serializedActivityState, cancellationToken) : default; dbContext.Entry(entity).Property("SerializedActivityState").CurrentValue = compressedSerializedActivityState; dbContext.Entry(entity).Property("SerializedActivityStateCompressionAlgorithm").CurrentValue = compressionAlgorithm; @@ -102,7 +102,7 @@ public class EFCoreActivityExecutionStore : IActivityExecutionStore if (!string.IsNullOrWhiteSpace(json)) { var compressionAlgorithm = (string?)dbContext.Entry(entity).Property("SerializedActivityStateCompressionAlgorithm").CurrentValue ?? nameof(None); - var compressionStrategy = _compressionStrategyResolver.Resolve(compressionAlgorithm); + var compressionStrategy = _compressionCodecResolver.Resolve(compressionAlgorithm); json = await compressionStrategy.DecompressAsync(json, cancellationToken); var dictionary = JsonSerializer.Deserialize>(json); return dictionary?.ToDictionary(x => x.Key, x => (object)x.Value); diff --git a/src/modules/Elsa.Workflows.Management/Compression/GZip.cs b/src/modules/Elsa.Workflows.Management/Compression/GZip.cs index 3e45b7257..90da46cc3 100644 --- a/src/modules/Elsa.Workflows.Management/Compression/GZip.cs +++ b/src/modules/Elsa.Workflows.Management/Compression/GZip.cs @@ -7,7 +7,7 @@ namespace Elsa.Workflows.Management.Compression; /// /// Represents a GZip compression strategy. /// -public class GZip : ICompressionStrategy +public class GZip : ICompressionCodec { /// public async ValueTask CompressAsync(string input, CancellationToken cancellationToken) diff --git a/src/modules/Elsa.Workflows.Management/Compression/None.cs b/src/modules/Elsa.Workflows.Management/Compression/None.cs index 3072c74b3..d06420f63 100644 --- a/src/modules/Elsa.Workflows.Management/Compression/None.cs +++ b/src/modules/Elsa.Workflows.Management/Compression/None.cs @@ -5,7 +5,7 @@ namespace Elsa.Workflows.Management.Compression; /// /// Represents a compression strategy that does not compress or decompress the input. /// -public class None : ICompressionStrategy +public class None : ICompressionCodec { /// public ValueTask CompressAsync(string input, CancellationToken cancellationToken) diff --git a/src/modules/Elsa.Workflows.Management/Compression/Zstd.cs b/src/modules/Elsa.Workflows.Management/Compression/Zstd.cs new file mode 100644 index 000000000..448ddeb18 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Compression/Zstd.cs @@ -0,0 +1,37 @@ +using System.Text; +using Elsa.Workflows.Management.Contracts; +using IronCompress; + +namespace Elsa.Workflows.Management.Compression; + +/// +/// Represents a ZSTD compression strategy. +/// +public class Zstd : ICompressionCodec +{ + private Iron Iron { get; set; } = new(); + + /// + public ValueTask CompressAsync(string input, CancellationToken cancellationToken = default) + { + var inputBytes = Encoding.UTF8.GetBytes(input); + var span = inputBytes.AsSpan(); + var result = Iron.Compress(Codec.Zstd, span); + var compressedBytes = result.AsSpan(); + var compressedString = Convert.ToBase64String(compressedBytes); + + return new (compressedString); + } + + /// + public ValueTask DecompressAsync(string input, CancellationToken cancellationToken = default) + { + var inputBytes = Convert.FromBase64String(input); + var span = inputBytes.AsSpan(); + var result = Iron.Decompress(Codec.Zstd, span); + var decompressedBytes = result.AsSpan(); + var decompressedString = Encoding.UTF8.GetString(decompressedBytes); + + return new (decompressedString); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Contracts/ICompressionStrategy.cs b/src/modules/Elsa.Workflows.Management/Contracts/ICompressionCodec.cs similarity index 92% rename from src/modules/Elsa.Workflows.Management/Contracts/ICompressionStrategy.cs rename to src/modules/Elsa.Workflows.Management/Contracts/ICompressionCodec.cs index d000ec2d7..f8bedefa1 100644 --- a/src/modules/Elsa.Workflows.Management/Contracts/ICompressionStrategy.cs +++ b/src/modules/Elsa.Workflows.Management/Contracts/ICompressionCodec.cs @@ -3,7 +3,7 @@ namespace Elsa.Workflows.Management.Contracts; /// /// Represents a compression strategy. /// -public interface ICompressionStrategy +public interface ICompressionCodec { /// /// Compresses the input. diff --git a/src/modules/Elsa.Workflows.Management/Contracts/ICompressionCodecResolver.cs b/src/modules/Elsa.Workflows.Management/Contracts/ICompressionCodecResolver.cs new file mode 100644 index 000000000..cb7d234fc --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Contracts/ICompressionCodecResolver.cs @@ -0,0 +1,12 @@ +namespace Elsa.Workflows.Management.Contracts; + +/// +/// Resolves a from its name. +/// +public interface ICompressionCodecResolver +{ + /// + /// Resolves a from its name. + /// + ICompressionCodec Resolve(string name); +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Contracts/ICompressionStrategyResolver.cs b/src/modules/Elsa.Workflows.Management/Contracts/ICompressionStrategyResolver.cs deleted file mode 100644 index df7824b3c..000000000 --- a/src/modules/Elsa.Workflows.Management/Contracts/ICompressionStrategyResolver.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Elsa.Workflows.Management.Contracts; - -/// -/// Resolves a from its name. -/// -public interface ICompressionStrategyResolver -{ - /// - /// Resolves a from its name. - /// - ICompressionStrategy Resolve(string name); -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Elsa.Workflows.Management.csproj b/src/modules/Elsa.Workflows.Management/Elsa.Workflows.Management.csproj index b539f922b..b0cc890fe 100644 --- a/src/modules/Elsa.Workflows.Management/Elsa.Workflows.Management.csproj +++ b/src/modules/Elsa.Workflows.Management/Elsa.Workflows.Management.csproj @@ -9,6 +9,7 @@ + diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index 5ce9ceb9f..df1288171 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -188,9 +188,10 @@ public class WorkflowManagementFeature : FeatureBase .AddScoped() .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() ; Services.AddNotificationHandlersFrom(GetType()); diff --git a/src/modules/Elsa.Workflows.Management/Services/CompressionCodecResolver.cs b/src/modules/Elsa.Workflows.Management/Services/CompressionCodecResolver.cs new file mode 100644 index 000000000..74c0e1501 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Services/CompressionCodecResolver.cs @@ -0,0 +1,24 @@ +using Elsa.Workflows.Management.Compression; +using Elsa.Workflows.Management.Contracts; + +namespace Elsa.Workflows.Management.Services; + +/// +public class CompressionCodecResolver : ICompressionCodecResolver +{ + private readonly IDictionary _codecs; + + /// + /// Initializes a new instance of the class. + /// + public CompressionCodecResolver(IEnumerable codecs) + { + _codecs = codecs.ToDictionary(c => c.GetType().Name); + } + + /// + public ICompressionCodec Resolve(string name) + { + return _codecs.TryGetValue(name, out var codec) ? codec : new None(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Services/CompressionStrategyResolver.cs b/src/modules/Elsa.Workflows.Management/Services/CompressionStrategyResolver.cs deleted file mode 100644 index bb435aa75..000000000 --- a/src/modules/Elsa.Workflows.Management/Services/CompressionStrategyResolver.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Elsa.Workflows.Management.Compression; -using Elsa.Workflows.Management.Contracts; - -namespace Elsa.Workflows.Management.Services; - -/// -public class CompressionStrategyResolver(IEnumerable strategies) : ICompressionStrategyResolver -{ - /// - public ICompressionStrategy Resolve(string name) - { - return strategies.FirstOrDefault(s => s.GetType().Name == name) ?? new None(); - } -} \ No newline at end of file