/// <summary>
    /// Removes the value with the given key.
    /// </summary>
    /// <param name="key">A string identifying the requested value.</param>
    public void Remove(string key)
    {
        var semaphore = GetOrCreateLock(key, null);

        semaphore.Wait();
        try
        {
            L2Cache.Remove(key);
            L1Cache.Remove(
                $"{L1L2RedisCacheOptions.KeyPrefix}{key}");
            MessagePublisher.Publish(key);
            L1Cache.Remove(
                $"{L1L2RedisCacheOptions.LockKeyPrefix}{key}");
        }
        finally
        {
            semaphore.Release();
        }
    }
    /// <summary>
    /// Removes the value with the given key.
    /// </summary>
    /// <param name="key">A string identifying the requested value.</param>
    /// <param name="cancellationToken">Optional. The System.Threading.CancellationToken used to propagate notifications that the operation should be canceled.</param>
    /// <returns>The System.Threading.Tasks.Task that represents the asynchronous operation.</returns>
    public async Task RemoveAsync(
        string key,
        CancellationToken cancellationToken = default)
    {
        var semaphore = await GetOrCreateLockAsync(
            key, null, cancellationToken);

        await semaphore.WaitAsync(cancellationToken);

        try
        {
            L2Cache.Remove(key);
            L1Cache.Remove(
                $"{L1L2RedisCacheOptions.KeyPrefix}{key}");
            MessagePublisher.Publish(key);
            L1Cache.Remove(
                $"{L1L2RedisCacheOptions.LockKeyPrefix}{key}");
        }
        finally
        {
            semaphore.Release();
        }
    }