private async Task StartInternalAsync(
            string lockName,
            LockLostBehavior lockLostBehavior,
            CancellationToken cancellationToken)
        {
            _log.TraceMethodEntry();

            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }

            var lockTimeout = TimeSpan.FromSeconds(5);

            using (var inMemoryLockCacheClient = new InMemoryCacheClient())
                using (var distributedLockCacheClient = new HybridCacheClient(inMemoryLockCacheClient, _messageBus))
                {
                    var lockProvider = new CacheLockProvider(distributedLockCacheClient, _messageBus);

                    while (!cancellationToken.IsCancellationRequested)
                    {
                        var lockHandle = await lockProvider.AcquireAsync(
                            lockName,
                            lockTimeout,
                            cancellationToken)
                                         .ConfigureAwait(false);

                        if (lockHandle != null)
                        {
                            try
                            {
                                // we have the lock
                                // keep renewing it
                                await HoldAndRenewAsync(
                                    lockHandle,
                                    cancellationToken)
                                .ConfigureAwait(false);
                            }
                            catch (Exception e)
                            {
                                switch (lockLostBehavior)
                                {
                                case LockLostBehavior.Complete:
                                    return;

                                case LockLostBehavior.Error:
                                    throw new LockLostException(lockName, e);

                                case LockLostBehavior.Retry:
                                    break;

                                default:
                                    break;
                                }
                            }
                        }
                    }
                }
        }
        /// <summary>
        /// Creates and starts a Distributed Lock Process Manager.
        /// </summary>
        /// <param name="messageBus">The message bus to attach to.</param>
        /// <param name="loggerFactory">The logging interface factory.</param>
        /// <param name="lockName">The name of the lock.</param>
        /// <param name="lockLostBehavior">The behavior to carry out if the lock is lost.</param>
        /// <param name="hasLockObserver">The observer for watching if the lock is aquired.</param>
        /// <param name="cancellationToken">The cancellation token used to stop the process manager.</param>
        /// <returns>Instance of the distributed lock process manager and the active task.</returns>
        public static (DistributedLockProcessManager Instance, Task Task, IDisposable HasLockSubscription) SubscribeAndStart(
            IMessageBus messageBus,
            ILoggerFactory loggerFactory,
            string lockName,
            LockLostBehavior lockLostBehavior,
            IObserver <bool> hasLockObserver,
            CancellationToken cancellationToken)
        {
            if (hasLockObserver == null)
            {
                throw new ArgumentNullException(nameof(hasLockObserver));
            }

            var instance = new DistributedLockProcessManager(
                messageBus,
                lockName,
                lockLostBehavior,
                loggerFactory);

            var hasLockSubscription = instance.HasLock.Subscribe(hasLockObserver);
            var task = instance.StartAsync(cancellationToken);

            return(instance, task, hasLockSubscription);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="DistributedLockProcessManager"/> class.
        /// </summary>
        /// <param name="messageBus">The message bus to attach to.</param>
        /// <param name="lockName">The name of the lock.</param>
        /// <param name="lockLostBehavior">The behavior to carry out if the lock is lost.</param>
        /// <param name="loggerFactory">The logging interface factory.</param>
        public DistributedLockProcessManager(
            IMessageBus messageBus,
            string lockName,
            LockLostBehavior lockLostBehavior,
            ILoggerFactory loggerFactory)
        {
            _messageBus = messageBus ?? throw new ArgumentNullException(nameof(messageBus));

            if (string.IsNullOrWhiteSpace(lockName))
            {
                throw new ArgumentNullException(nameof(lockName));
            }

            _lockName = lockName;

            _lockLostBehaviour = lockLostBehavior;

            if (loggerFactory == null)
            {
                throw new ArgumentNullException(nameof(loggerFactory));
            }

            _log = loggerFactory.CreateLogger <DistributedLockProcessManager>();
        }