< Summary

Information
Class: Elsa.Workflows.Runtime.Distributed.DistributedRuntimeLockProviderValidator
Assembly: Elsa.Workflows.Runtime.Distributed
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime.Distributed/Services/DistributedRuntimeLockProviderValidator.cs
Line coverage
52%
Covered lines: 43
Uncovered lines: 39
Coverable lines: 82
Total lines: 167
Line coverage: 52.4%
Branch coverage
51%
Covered branches: 27
Total branches: 52
Branch coverage: 51.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Validate()50%15852.63%
FindLocalProviderType(...)85.71%151483.33%
GetInnerProviders()37.5%1031630.3%
TryGetDistributedLockProviderElementType(...)35.71%231464.28%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime.Distributed/Services/DistributedRuntimeLockProviderValidator.cs

#LineLine coverage
 1using System.Linq;
 2using System.Reflection;
 3using Elsa.Common.DistributedHosting;
 4using Elsa.Common.DistributedHosting.DistributedLocks;
 5using Medallion.Threading;
 6using Medallion.Threading.FileSystem;
 7using Microsoft.Extensions.Logging;
 8using Microsoft.Extensions.Options;
 9
 10namespace Elsa.Workflows.Runtime.Distributed;
 11
 12/// <summary>
 13/// Logs when the distributed runtime is backed by a local-only lock provider.
 14/// </summary>
 1415public class DistributedRuntimeLockProviderValidator(
 1416    IDistributedLockProvider distributedLockProvider,
 1417    IOptions<DistributedLockingOptions> options,
 1418    ILogger<DistributedRuntimeLockProviderValidator> logger)
 19{
 20    /// <summary>
 21    /// Checks the configured provider.
 22    /// </summary>
 23    public void Validate()
 24    {
 1425        var localProviderType = FindLocalProviderType(distributedLockProvider);
 1426        var configuredProviderTypeName = distributedLockProvider.GetType().FullName ?? distributedLockProvider.GetType()
 27
 1428        if (localProviderType == null)
 29        {
 030            logger.LogDebug(
 031                "Distributed workflow runtime lock provider {DistributedLockProviderType} is configured. Ensure this pro
 032                configuredProviderTypeName);
 033            return;
 34        }
 35
 1436        var localProviderTypeName = localProviderType.FullName ?? localProviderType.Name;
 37
 1438        if (options.Value.AllowLocalLockProviderInDistributedRuntime)
 39        {
 1440            logger.LogDebug(
 1441                "Distributed workflow runtime is using acknowledged local-only lock provider {LocalDistributedLockProvid
 1442                localProviderTypeName,
 1443                configuredProviderTypeName);
 1444            return;
 45        }
 46
 047        logger.LogWarning(
 048            "Distributed workflow runtime is using local-only lock provider {LocalDistributedLockProviderType} through c
 049            localProviderTypeName,
 050            configuredProviderTypeName);
 051    }
 52
 53    private Type? FindLocalProviderType(IDistributedLockProvider provider, ISet<IDistributedLockProvider>? visited = nul
 54    {
 2655        visited ??= new HashSet<IDistributedLockProvider>(ReferenceEqualityComparer.Instance);
 56
 2657        if (!visited.Add(provider))
 058            return null;
 59
 2660        var providerType = provider.GetType();
 61
 2662        if (provider is FileDistributedSynchronizationProvider or NoopDistributedSynchronizationProvider)
 1463            return providerType;
 64
 3665        foreach (var innerProvider in GetInnerProviders(provider))
 66        {
 1267            var localProviderType = FindLocalProviderType(innerProvider, visited);
 68
 1269            if (localProviderType != null)
 1270                return localProviderType;
 71        }
 72
 073        return null;
 1274    }
 75
 76    private IEnumerable<IDistributedLockProvider> GetInnerProviders(IDistributedLockProvider provider)
 77    {
 3678        foreach (var property in provider.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
 79            .Where(property => property.GetIndexParameters().Length == 0))
 80        {
 1281            var returnsDistributedLockProvider = typeof(IDistributedLockProvider).IsAssignableFrom(property.PropertyType
 1282            var returnsDistributedLockProviders =
 1283                property.PropertyType == typeof(System.Collections.IEnumerable) ||
 1284                TryGetDistributedLockProviderElementType(property.PropertyType, out _);
 85
 1286            if (!returnsDistributedLockProvider && !returnsDistributedLockProviders)
 87                continue;
 88
 89            object? value;
 90            try
 91            {
 1292                value = property.GetValue(provider);
 1293            }
 094            catch (TargetInvocationException ex)
 95            {
 096                logger.LogDebug(ex, "Skipping distributed lock provider property {PropertyName} because the getter threw
 097                continue;
 98            }
 99
 12100            if (value is IDistributedLockProvider innerProvider)
 101            {
 12102                yield return innerProvider;
 103            }
 0104            else if (value is System.Collections.IEnumerable innerProviders)
 105            {
 106                System.Collections.IEnumerator enumerator;
 107
 108                try
 109                {
 0110                    enumerator = innerProviders.GetEnumerator();
 0111                }
 0112                catch (Exception ex) when (ex is InvalidOperationException or ObjectDisposedException or NotSupportedExc
 113                {
 0114                    logger.LogDebug(ex, "Skipping distributed lock provider property {PropertyName} because enumeration 
 0115                    continue;
 116                }
 117
 0118                using var disposableEnumerator = enumerator as IDisposable;
 119
 0120                while (true)
 121                {
 122                    object? providerItem;
 123
 124                    try
 125                    {
 0126                        if (!enumerator.MoveNext())
 0127                            break;
 128
 0129                        providerItem = enumerator.Current;
 0130                    }
 0131                    catch (Exception ex) when (ex is InvalidOperationException or ObjectDisposedException or NotSupporte
 132                    {
 0133                        logger.LogDebug(ex, "Skipping distributed lock provider property {PropertyName} because enumerat
 0134                        break;
 135                    }
 136
 0137                    if (providerItem is IDistributedLockProvider distributedLockProvider)
 0138                        yield return distributedLockProvider;
 139                }
 0140            }
 0141        }
 0142    }
 143
 144    private static bool TryGetDistributedLockProviderElementType(Type type, out Type? elementType)
 145    {
 12146        elementType = null;
 147
 12148        if (type.IsArray)
 149        {
 0150            elementType = type.GetElementType();
 0151            return elementType != null && typeof(IDistributedLockProvider).IsAssignableFrom(elementType);
 152        }
 153
 12154        if (type == typeof(string))
 0155            return false;
 156
 12157        elementType = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)
 12158            ? type.GetGenericArguments()[0]
 12159            : type
 12160                .GetInterfaces()
 0161                .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
 0162                .Select(x => x.GetGenericArguments()[0])
 12163                .FirstOrDefault(x => typeof(IDistributedLockProvider).IsAssignableFrom(x));
 164
 12165        return elementType != null && typeof(IDistributedLockProvider).IsAssignableFrom(elementType);
 166    }
 167}