/// <inheritdoc/>
        public void Dispose()
        {
            if (_cancellationTokenSource != null)
            {
                _cancellationTokenSource.Cancel(false);
                _cancellationTokenSource.Dispose();
                _cancellationTokenSource = null;
            }

            if (_cas == 0)
            {
                // Never locked
                return;
            }

            var key = LockDocument.GetKey(Name);

            _collection.RemoveAsync(key, new RemoveOptions().Cas(_cas))
            .ContinueWith(t =>
            {
                if (t.Exception !.InnerExceptions.OfType <DocumentNotFoundException>().Any())
                {
                    _logger.LogDebug("Did not release lock '{name}' for holder '{holder}' because it was already released.",
                                     Name,
                                     Holder);
                }
        /// <inheritdoc/>
        public void Dispose()
        {
            if (_cancellationTokenSource != null)
            {
                _cancellationTokenSource.Cancel(false);
                _cancellationTokenSource.Dispose();
                _cancellationTokenSource = null;
            }

            if (_cas == 0)
            {
                // Never locked
                return;
            }

            var key = LockDocument.GetKey(Name);

            _bucket.RemoveAsync(key, _cas)
            .ContinueWith(t =>
            {
                if (t.IsFaulted)
                {
                    _log.Warn($"Error releasing lock '{Name}' for holder '{Holder}'", t.Exception);
                }
                else if (t.Result != null)
                {
                    var result = t.Result;
                    if (result.Status == ResponseStatus.KeyNotFound)
                    {
                        _log.Debug("Did not release lock '{0}' for holder '{1}' because it was already released.",
                                   Name,
                                   Holder);
                    }
                    else if (result.Status == ResponseStatus.DocumentMutationDetected)
                    {
                        _log.Debug(
                            "Did not release lock '{0}' for holder '{1}' because it was already held by another.",
                            Name,
                            Holder);
                    }
                    else if (!result.Success)
                    {
                        _log.Warn("Error releasing lock '{0}' for holder '{1}': {2}", Name, Holder,
                                  result.Exception?.Message ?? result.Message);
                    }
                }
            });
        }
        /// <inheritdoc/>
        public async Task Renew(TimeSpan expiration, CancellationToken cancellationToken = default)
        {
            if (expiration <= TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(expiration), "Value must be positive.");
            }

            var key      = LockDocument.GetKey(Name);
            var document = new LockDocument
            {
                Holder            = Holder,
                RequestedDateTime = DateTime.UtcNow
            };

            bool            lockAcquired = false;
            IMutationResult?result       = null;

            try
            {
                if (_cas == 0)
                {
                    // We're creating a new lock
                    _logger.LogDebug("Requesting lock '{name}' for holder '{holder}' for {expiration}", Name, Holder,
                                     expiration);
                    result = await _collection.InsertAsync(key, document,
                                                           new InsertOptions().Expiry(expiration).CancellationToken(cancellationToken))
                             .ConfigureAwait(false);
                }
                else
                {
                    _logger.LogDebug("Renewing lock '{name}' for holder '{holder}' for {expiration}", Name, Holder,
                                     expiration);
                    result = await _collection.ReplaceAsync(key, document,
                                                            new ReplaceOptions().Expiry(expiration).CancellationToken(cancellationToken).Cas(_cas))
                             .ConfigureAwait(false);
                }

                lockAcquired = true;
            }
            catch (CasMismatchException)
            {
                // This is a valid case, trap the exception
                _logger.LogDebug("CAS mismatch updating lock '{name}'", Name);
            }
            catch (DocumentExistsException)
            {
                // This is a valid case, trap the exception
                _logger.LogDebug("Lock document already exists for lock '{name}'", Name);
            }
            catch (DocumentNotFoundException)
            {
                // This is a valid, but rare, case where the document being renewed expired before
                // the renewal. In this case, we'll let the logic move on, which will recreate the document.
                _logger.LogDebug("Lock document missing for lock '{name}'", Name);
            }

            if (lockAcquired)
            {
                _logger.LogDebug("Lock '{name}' issued to holder '{holder}'", Name, Holder);

                _expirationInterval = expiration;
                _cas = result !.Cas;

                return;
            }

            if (!lockAcquired)
            {
                _logger.LogDebug("Lock '{name}' unavailable, getting lock info", Name);

                IGetResult getResult;
                try
                {
                    getResult = await _collection.GetAsync(key).ConfigureAwait(false);
                }
                catch (DocumentNotFoundException)
                {
                    try
                    {
                        // Couldn't find the lock, must have expired between Insert and Get, try one more time
                        result = await _collection.InsertAsync(key, document,
                                                               new InsertOptions().Expiry(expiration).CancellationToken(cancellationToken))
                                 .ConfigureAwait(false);
                    }
                    catch (DocumentExistsException)
                    {
                        throw new CouchbaseLockUnavailableException(Name);
                    }

                    _logger.LogDebug("Lock '{name}' issued to holder '{holder}'", Name, Holder);

                    _expirationInterval = expiration;
                    _cas = result.Cas;
                    return;
                }

                _logger.LogDebug("Unable to acquire lock '{name}' for holder '{holder}'", Name, Holder);

                throw new CouchbaseLockUnavailableException(Name)
                      {
                          Holder = getResult.ContentAs <LockDocument>().Holder
                      };
            }
        }
        /// <inheritdoc/>
        public async Task Renew(TimeSpan expiration)
        {
            if (expiration <= TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(expiration), "Value must be positive.");
            }

            var key      = LockDocument.GetKey(Name);
            var document = new LockDocument
            {
                Holder            = Holder,
                RequestedDateTime = DateTime.UtcNow
            };

            IOperationResult <LockDocument> result;

            if (_cas == 0)
            {
                // We're creating a new lock
                _log.Debug("Requesting lock '{0}' for holder '{1}' for {2}", Name, Holder, expiration);
                result = await _bucket.InsertAsync(key, document, expiration).ConfigureAwait(false);
            }
            else
            {
                _log.Debug("Renewing lock '{0}' for holder '{1}' for {2}", Name, Holder, expiration);
                result = await _bucket.UpsertAsync(key, document, _cas, expiration).ConfigureAwait(false);
            }

            if (result.Status == ResponseStatus.DocumentMutationDetected || result.Status == ResponseStatus.KeyExists)
            {
                _log.Debug("Lock '{0}' unavailable, getting lock info", Name);

                var getResult = await _bucket.GetDocumentAsync <LockDocument>(key).ConfigureAwait(false);

                if (getResult.Status == ResponseStatus.KeyNotFound)
                {
                    // Couldn't find the lock, must have expired between Insert and Get, try one more time
                    result = await _bucket.InsertAsync(key, document, expiration).ConfigureAwait(false);

                    if (result.Status == ResponseStatus.KeyExists)
                    {
                        throw new CouchbaseLockUnavailableException(Name);
                    }

                    _log.Debug("Lock '{0}' issued to holder '{1}'", Name, Holder);
                    result.EnsureSuccess();

                    _expirationInterval = expiration;
                    _cas = result.Cas;
                    return;
                }

                getResult.EnsureSuccess();

                _log.Debug("Unable to acquire lock '{0}' for holder '{1}'", Name, Holder);

                throw new CouchbaseLockUnavailableException(Name)
                      {
                          Holder = getResult.Content.Holder
                      };
            }

            _log.Debug("Lock '{0}' issued to holder '{1}'", Name, Holder);
            result.EnsureSuccess();

            _expirationInterval = expiration;
            _cas = result.Cas;
        }