diff --git a/Caching.sln b/Caching.sln index 0f06fc96..633f74e2 100644 --- a/Caching.sln +++ b/Caching.sln @@ -24,12 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemoryCacheConcurencySample EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProfilingSample", "samples\ProfilingSample\ProfilingSample.csproj", "{FD9FD44E-50D7-4121-9DAB-87A88191CE9F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Redis", "src\Microsoft.Extensions.Caching.Redis\Microsoft.Extensions.Caching.Redis.csproj", "{02E23070-947C-4438-8E50-CC2456D237BA}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedisCacheSample", "samples\RedisCacheSample\RedisCacheSample.csproj", "{4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Redis.Tests", "test\Microsoft.Extensions.Caching.Redis.Tests\Microsoft.Extensions.Caching.Redis.Tests.csproj", "{D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Abstractions", "src\Microsoft.Extensions.Caching.Abstractions\Microsoft.Extensions.Caching.Abstractions.csproj", "{60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.SqlServer", "src\Microsoft.Extensions.Caching.SqlServer\Microsoft.Extensions.Caching.SqlServer.csproj", "{4301BDAE-CD67-4C1F-B754-D979C7274305}" @@ -122,18 +118,6 @@ Global {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.Build.0 = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -146,18 +130,6 @@ Global {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -264,9 +236,7 @@ Global {1C24C0CE-84B6-4D6C-B484-32741D704FD8} = {2E14F082-1A04-4E7C-9162-1675119F9A9E} {FF7C7587-BBCA-4574-95AE-101A6FF63B8F} = {459E1593-2C11-42CB-AD17-F7597E69E5D2} {FD9FD44E-50D7-4121-9DAB-87A88191CE9F} = {459E1593-2C11-42CB-AD17-F7597E69E5D2} - {02E23070-947C-4438-8E50-CC2456D237BA} = {5A04042D-438D-407B-B239-0CAD6A2BD8C2} {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57} = {459E1593-2C11-42CB-AD17-F7597E69E5D2} - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5} = {2E14F082-1A04-4E7C-9162-1675119F9A9E} {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6} = {5A04042D-438D-407B-B239-0CAD6A2BD8C2} {4301BDAE-CD67-4C1F-B754-D979C7274305} = {5A04042D-438D-407B-B239-0CAD6A2BD8C2} {FED2ABB6-D0B3-4AC7-940F-250C9767EACB} = {459E1593-2C11-42CB-AD17-F7597E69E5D2} diff --git a/build/dependencies.props b/build/dependencies.props index 9e9aba3b..412440cb 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -18,7 +18,6 @@ 15.6.1 4.9.0 2.0.3 - 1.2.6 2.0.513 4.6.0-preview1-26907-04 2.3.1 diff --git a/samples/RedisCacheSample/Program.cs b/samples/RedisCacheSample/Program.cs index 17b41e00..215f2951 100644 --- a/samples/RedisCacheSample/Program.cs +++ b/samples/RedisCacheSample/Program.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Caching.Redis; +using Microsoft.Extensions.Caching.StackExchangeRedis; namespace RedisCacheSample { diff --git a/samples/RedisCacheSample/RedisCacheSample.csproj b/samples/RedisCacheSample/RedisCacheSample.csproj index 20fe7828..43a05551 100644 --- a/samples/RedisCacheSample/RedisCacheSample.csproj +++ b/samples/RedisCacheSample/RedisCacheSample.csproj @@ -1,4 +1,4 @@ - + net461;netcoreapp2.2 @@ -6,7 +6,7 @@ - + diff --git a/src/Microsoft.Extensions.Caching.Redis/Microsoft.Extensions.Caching.Redis.csproj b/src/Microsoft.Extensions.Caching.Redis/Microsoft.Extensions.Caching.Redis.csproj deleted file mode 100644 index 0410ea27..00000000 --- a/src/Microsoft.Extensions.Caching.Redis/Microsoft.Extensions.Caching.Redis.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Distributed cache implementation of Microsoft.Extensions.Caching.Distributed.IDistributedCache using Redis. - netstandard2.0 - $(NoWarn);CS1591 - true - cache;distributedcache;redis - - - - - - - - - - - - diff --git a/src/Microsoft.Extensions.Caching.Redis/RedisCache.cs b/src/Microsoft.Extensions.Caching.Redis/RedisCache.cs deleted file mode 100644 index bd9f7140..00000000 --- a/src/Microsoft.Extensions.Caching.Redis/RedisCache.cs +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Options; -using StackExchange.Redis; - -namespace Microsoft.Extensions.Caching.Redis -{ - public class RedisCache : IDistributedCache, IDisposable - { - // KEYS[1] = = key - // ARGV[1] = absolute-expiration - ticks as long (-1 for none) - // ARGV[2] = sliding-expiration - ticks as long (-1 for none) - // ARGV[3] = relative-expiration (long, in seconds, -1 for none) - Min(absolute-expiration - Now, sliding-expiration) - // ARGV[4] = data - byte[] - // this order should not change LUA script depends on it - private const string SetScript = (@" - redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4]) - if ARGV[3] ~= '-1' then - redis.call('EXPIRE', KEYS[1], ARGV[3]) - end - return 1"); - private const string AbsoluteExpirationKey = "absexp"; - private const string SlidingExpirationKey = "sldexp"; - private const string DataKey = "data"; - private const long NotPresent = -1; - - private volatile ConnectionMultiplexer _connection; - private IDatabase _cache; - - private readonly RedisCacheOptions _options; - private readonly string _instance; - - private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1); - - public RedisCache(IOptions optionsAccessor) - { - if (optionsAccessor == null) - { - throw new ArgumentNullException(nameof(optionsAccessor)); - } - - _options = optionsAccessor.Value; - - // This allows partitioning a single backend cache for use with multiple apps/services. - _instance = _options.InstanceName ?? string.Empty; - } - - public byte[] Get(string key) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - return GetAndRefresh(key, getData: true); - } - - public async Task GetAsync(string key, CancellationToken token = default(CancellationToken)) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - token.ThrowIfCancellationRequested(); - - return await GetAndRefreshAsync(key, getData: true, token: token); - } - - public void Set(string key, byte[] value, DistributedCacheEntryOptions options) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - Connect(); - - var creationTime = DateTimeOffset.UtcNow; - - var absoluteExpiration = GetAbsoluteExpiration(creationTime, options); - - var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key }, - new RedisValue[] - { - absoluteExpiration?.Ticks ?? NotPresent, - options.SlidingExpiration?.Ticks ?? NotPresent, - GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent, - value - }); - } - - public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken)) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - token.ThrowIfCancellationRequested(); - - await ConnectAsync(token); - - var creationTime = DateTimeOffset.UtcNow; - - var absoluteExpiration = GetAbsoluteExpiration(creationTime, options); - - await _cache.ScriptEvaluateAsync(SetScript, new RedisKey[] { _instance + key }, - new RedisValue[] - { - absoluteExpiration?.Ticks ?? NotPresent, - options.SlidingExpiration?.Ticks ?? NotPresent, - GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent, - value - }); - } - - public void Refresh(string key) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - GetAndRefresh(key, getData: false); - } - - public async Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - token.ThrowIfCancellationRequested(); - - await GetAndRefreshAsync(key, getData: false, token: token); - } - - private void Connect() - { - if (_cache != null) - { - return; - } - - _connectionLock.Wait(); - try - { - if (_cache == null) - { - if (_options.ConfigurationOptions != null) - { - _connection = ConnectionMultiplexer.Connect(_options.ConfigurationOptions); - } - else - { - _connection = ConnectionMultiplexer.Connect(_options.Configuration); - } - _cache = _connection.GetDatabase(); - } - } - finally - { - _connectionLock.Release(); - } - } - - private async Task ConnectAsync(CancellationToken token = default(CancellationToken)) - { - token.ThrowIfCancellationRequested(); - - if (_cache != null) - { - return; - } - - await _connectionLock.WaitAsync(token); - try - { - if (_cache == null) - { - if (_options.ConfigurationOptions != null) - { - _connection = await ConnectionMultiplexer.ConnectAsync(_options.ConfigurationOptions); - } - else - { - _connection = await ConnectionMultiplexer.ConnectAsync(_options.Configuration); - } - - _cache = _connection.GetDatabase(); - } - } - finally - { - _connectionLock.Release(); - } - } - - private byte[] GetAndRefresh(string key, bool getData) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - Connect(); - - // This also resets the LRU status as desired. - // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math. - RedisValue[] results; - if (getData) - { - results = _cache.HashMemberGet(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey, DataKey); - } - else - { - results = _cache.HashMemberGet(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey); - } - - // TODO: Error handling - if (results.Length >= 2) - { - MapMetadata(results, out DateTimeOffset? absExpr, out TimeSpan? sldExpr); - Refresh(key, absExpr, sldExpr); - } - - if (results.Length >= 3 && results[2].HasValue) - { - return results[2]; - } - - return null; - } - - private async Task GetAndRefreshAsync(string key, bool getData, CancellationToken token = default(CancellationToken)) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - token.ThrowIfCancellationRequested(); - - await ConnectAsync(token); - - // This also resets the LRU status as desired. - // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math. - RedisValue[] results; - if (getData) - { - results = await _cache.HashMemberGetAsync(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey, DataKey); - } - else - { - results = await _cache.HashMemberGetAsync(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey); - } - - // TODO: Error handling - if (results.Length >= 2) - { - MapMetadata(results, out DateTimeOffset? absExpr, out TimeSpan? sldExpr); - await RefreshAsync(key, absExpr, sldExpr, token); - } - - if (results.Length >= 3 && results[2].HasValue) - { - return results[2]; - } - - return null; - } - - public void Remove(string key) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - Connect(); - - _cache.KeyDelete(_instance + key); - // TODO: Error handling - } - - public async Task RemoveAsync(string key, CancellationToken token = default(CancellationToken)) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - await ConnectAsync(token); - - await _cache.KeyDeleteAsync(_instance + key); - // TODO: Error handling - } - - private void MapMetadata(RedisValue[] results, out DateTimeOffset? absoluteExpiration, out TimeSpan? slidingExpiration) - { - absoluteExpiration = null; - slidingExpiration = null; - var absoluteExpirationTicks = (long?)results[0]; - if (absoluteExpirationTicks.HasValue && absoluteExpirationTicks.Value != NotPresent) - { - absoluteExpiration = new DateTimeOffset(absoluteExpirationTicks.Value, TimeSpan.Zero); - } - var slidingExpirationTicks = (long?)results[1]; - if (slidingExpirationTicks.HasValue && slidingExpirationTicks.Value != NotPresent) - { - slidingExpiration = new TimeSpan(slidingExpirationTicks.Value); - } - } - - private void Refresh(string key, DateTimeOffset? absExpr, TimeSpan? sldExpr) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - // Note Refresh has no effect if there is just an absolute expiration (or neither). - TimeSpan? expr = null; - if (sldExpr.HasValue) - { - if (absExpr.HasValue) - { - var relExpr = absExpr.Value - DateTimeOffset.Now; - expr = relExpr <= sldExpr.Value ? relExpr : sldExpr; - } - else - { - expr = sldExpr; - } - _cache.KeyExpire(_instance + key, expr); - // TODO: Error handling - } - } - - private async Task RefreshAsync(string key, DateTimeOffset? absExpr, TimeSpan? sldExpr, CancellationToken token = default(CancellationToken)) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - token.ThrowIfCancellationRequested(); - - // Note Refresh has no effect if there is just an absolute expiration (or neither). - TimeSpan? expr = null; - if (sldExpr.HasValue) - { - if (absExpr.HasValue) - { - var relExpr = absExpr.Value - DateTimeOffset.Now; - expr = relExpr <= sldExpr.Value ? relExpr : sldExpr; - } - else - { - expr = sldExpr; - } - await _cache.KeyExpireAsync(_instance + key, expr); - // TODO: Error handling - } - } - - private static long? GetExpirationInSeconds(DateTimeOffset creationTime, DateTimeOffset? absoluteExpiration, DistributedCacheEntryOptions options) - { - if (absoluteExpiration.HasValue && options.SlidingExpiration.HasValue) - { - return (long)Math.Min( - (absoluteExpiration.Value - creationTime).TotalSeconds, - options.SlidingExpiration.Value.TotalSeconds); - } - else if (absoluteExpiration.HasValue) - { - return (long)(absoluteExpiration.Value - creationTime).TotalSeconds; - } - else if (options.SlidingExpiration.HasValue) - { - return (long)options.SlidingExpiration.Value.TotalSeconds; - } - return null; - } - - private static DateTimeOffset? GetAbsoluteExpiration(DateTimeOffset creationTime, DistributedCacheEntryOptions options) - { - if (options.AbsoluteExpiration.HasValue && options.AbsoluteExpiration <= creationTime) - { - throw new ArgumentOutOfRangeException( - nameof(DistributedCacheEntryOptions.AbsoluteExpiration), - options.AbsoluteExpiration.Value, - "The absolute expiration value must be in the future."); - } - var absoluteExpiration = options.AbsoluteExpiration; - if (options.AbsoluteExpirationRelativeToNow.HasValue) - { - absoluteExpiration = creationTime + options.AbsoluteExpirationRelativeToNow; - } - - return absoluteExpiration; - } - - public void Dispose() - { - if (_connection != null) - { - _connection.Close(); - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Redis/RedisCacheOptions.cs b/src/Microsoft.Extensions.Caching.Redis/RedisCacheOptions.cs deleted file mode 100644 index 6faa779a..00000000 --- a/src/Microsoft.Extensions.Caching.Redis/RedisCacheOptions.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.Extensions.Options; -using StackExchange.Redis; - -namespace Microsoft.Extensions.Caching.Redis -{ - /// - /// Configuration options for . - /// - public class RedisCacheOptions : IOptions - { - /// - /// The configuration used to connect to Redis. - /// - public string Configuration { get; set; } - - /// - /// The configuration used to connect to Redis. - /// This is preferred over Configuration. - /// - public ConfigurationOptions ConfigurationOptions { get; set; } - - /// - /// The Redis instance name. - /// - public string InstanceName { get; set; } - - RedisCacheOptions IOptions.Value - { - get { return this; } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Redis/RedisExtensions.cs b/src/Microsoft.Extensions.Caching.Redis/RedisExtensions.cs deleted file mode 100644 index 40e71163..00000000 --- a/src/Microsoft.Extensions.Caching.Redis/RedisExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading.Tasks; -using StackExchange.Redis; - -namespace Microsoft.Extensions.Caching.Redis -{ - internal static class RedisExtensions - { - private const string HmGetScript = (@"return redis.call('HMGET', KEYS[1], unpack(ARGV))"); - - internal static RedisValue[] HashMemberGet(this IDatabase cache, string key, params string[] members) - { - var result = cache.ScriptEvaluate( - HmGetScript, - new RedisKey[] { key }, - GetRedisMembers(members)); - - // TODO: Error checking? - return (RedisValue[])result; - } - - internal static async Task HashMemberGetAsync( - this IDatabase cache, - string key, - params string[] members) - { - var result = await cache.ScriptEvaluateAsync( - HmGetScript, - new RedisKey[] { key }, - GetRedisMembers(members)); - - // TODO: Error checking? - return (RedisValue[])result; - } - - private static RedisValue[] GetRedisMembers(params string[] members) - { - var redisMembers = new RedisValue[members.Length]; - for (int i = 0; i < members.Length; i++) - { - redisMembers[i] = (RedisValue)members[i]; - } - - return redisMembers; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Redis/StackExchangeRedisCacheServiceCollectionExtensions.cs b/src/Microsoft.Extensions.Caching.Redis/StackExchangeRedisCacheServiceCollectionExtensions.cs deleted file mode 100644 index 9a3c2f93..00000000 --- a/src/Microsoft.Extensions.Caching.Redis/StackExchangeRedisCacheServiceCollectionExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Caching.Redis; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.DependencyInjection -{ - /// - /// Extension methods for setting up Redis distributed cache related services in an . - /// - public static class RedisCacheServiceCollectionExtensions - { - /// - /// Adds Redis distributed caching services to the specified . - /// - /// The to add services to. - /// An to configure the provided - /// . - /// The so that additional calls can be chained. - public static IServiceCollection AddDistributedRedisCache(this IServiceCollection services, Action setupAction) - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (setupAction == null) - { - throw new ArgumentNullException(nameof(setupAction)); - } - - services.AddOptions(); - services.Configure(setupAction); - services.Add(ServiceDescriptor.Singleton()); - - return services; - } - } -} diff --git a/src/Microsoft.Extensions.Caching.Redis/baseline.netcore.json b/src/Microsoft.Extensions.Caching.Redis/baseline.netcore.json deleted file mode 100644 index 562024a8..00000000 --- a/src/Microsoft.Extensions.Caching.Redis/baseline.netcore.json +++ /dev/null @@ -1,295 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Caching.Redis, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.DependencyInjection.RedisCacheServiceCollectionExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "AddDistributedRedisCache", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - }, - { - "Name": "setupAction", - "Type": "System.Action" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Caching.Redis.RedisCache", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "System.IDisposable" - ], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - } - ], - "ReturnType": "System.Byte[]", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAsync", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "token", - "Type": "System.Threading.CancellationToken", - "DefaultValue": "default(System.Threading.CancellationToken)" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Set", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.Byte[]" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Caching.Distributed.DistributedCacheEntryOptions" - } - ], - "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "SetAsync", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.Byte[]" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Caching.Distributed.DistributedCacheEntryOptions" - }, - { - "Name": "token", - "Type": "System.Threading.CancellationToken", - "DefaultValue": "default(System.Threading.CancellationToken)" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Refresh", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "RefreshAsync", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "token", - "Type": "System.Threading.CancellationToken", - "DefaultValue": "default(System.Threading.CancellationToken)" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Remove", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "RemoveAsync", - "Parameters": [ - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "token", - "Type": "System.Threading.CancellationToken", - "DefaultValue": "default(System.Threading.CancellationToken)" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Caching.Distributed.IDistributedCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Dispose", - "Parameters": [], - "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "System.IDisposable", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "optionsAccessor", - "Type": "Microsoft.Extensions.Options.IOptions" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Caching.Redis.RedisCacheOptions", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Options.IOptions" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Configuration", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Configuration", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_InstanceName", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_InstanceName", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/CacheServiceExtensionsTests.cs b/test/Microsoft.Extensions.Caching.Redis.Tests/CacheServiceExtensionsTests.cs deleted file mode 100644 index b83369f6..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/CacheServiceExtensionsTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Xunit; - -namespace Microsoft.Extensions.Caching.Redis -{ - public class CacheServiceExtensionsTests - { - [Fact] - public void AddDistributedRedisCache_RegistersDistributedCacheAsSingleton() - { - // Arrange - var services = new ServiceCollection(); - - // Act - services.AddDistributedRedisCache(options => { }); - - // Assert - var distributedCache = services.FirstOrDefault(desc => desc.ServiceType == typeof(IDistributedCache)); - - Assert.NotNull(distributedCache); - Assert.Equal(ServiceLifetime.Singleton, distributedCache.Lifetime); - } - - [Fact] - public void AddDistributedRedisCache_ReplacesPreviouslyUserRegisteredServices() - { - // Arrange - var services = new ServiceCollection(); - services.AddScoped(typeof(IDistributedCache), sp => Mock.Of()); - - // Act - services.AddDistributedRedisCache(options => { }); - - // Assert - var serviceProvider = services.BuildServiceProvider(); - - var distributedCache = services.FirstOrDefault(desc => desc.ServiceType == typeof(IDistributedCache)); - - Assert.NotNull(distributedCache); - Assert.Equal(ServiceLifetime.Scoped, distributedCache.Lifetime); - Assert.IsType(serviceProvider.GetRequiredService()); - } - - [Fact] - public void AddDistributedRedisCache_allows_chaining() - { - var services = new ServiceCollection(); - - Assert.Same(services, services.AddDistributedRedisCache(_ => { })); - } - } -} diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisTestConfig.cs b/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisTestConfig.cs deleted file mode 100644 index 4ac9c62a..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisTestConfig.cs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using Microsoft.Extensions.Caching.Distributed; - -namespace Microsoft.Extensions.Caching.Redis -{ - public static class RedisTestConfig - { - internal const string RedisServerExeName = "redis-server.exe"; - internal const string FunctionalTestsRedisServerExeName = "RedisFuncTests-redis-server"; - internal const string UserProfileRedisNugetPackageServerPath = @".dotnet\packages\Redis-64\2.8.9"; - internal const string CIMachineRedisNugetPackageServerPath = @"Redis-64\2.8.9"; - - private static volatile Process _redisServerProcess; // null implies if server exists it was not started by this code - private static readonly object _redisServerProcessLock = new object(); - public static int RedisPort = 6379; // override default so that do not interfere with anyone else's server - - public static IDistributedCache CreateCacheInstance(string instanceName) - { - return new RedisCache(new RedisCacheOptions() - { - Configuration = "localhost:" + RedisPort, - InstanceName = instanceName, - }); - } - - public static void GetOrStartServer() - { - if (UserHasStartedOwnRedisServer()) - { - // user claims they have started their own - return; - } - - if (AlreadyOwnRunningRedisServer()) - { - return; - } - - TryConnectToOrStartServer(); - } - - private static bool AlreadyOwnRunningRedisServer() - { - // Does RedisTestConfig already know about a running server? - if (_redisServerProcess != null - && !_redisServerProcess.HasExited) - { - return true; - } - - return false; - } - - private static bool TryConnectToOrStartServer() - { - if (CanFindExistingRedisServer()) - { - return true; - } - - return TryStartRedisServer(); - } - - public static void StopRedisServer() - { - if (UserHasStartedOwnRedisServer()) - { - // user claims they have started their own - they are responsible for stopping it - return; - } - - if (CanFindExistingRedisServer()) - { - lock (_redisServerProcessLock) - { - if (_redisServerProcess != null) - { - _redisServerProcess.Kill(); - _redisServerProcess = null; - } - } - } - } - - private static bool CanFindExistingRedisServer() - { - var process = Process.GetProcessesByName(FunctionalTestsRedisServerExeName).SingleOrDefault(); - if (process == null || process.HasExited) - { - lock (_redisServerProcessLock) - { - _redisServerProcess = null; - } - return false; - } - - lock (_redisServerProcessLock) - { - _redisServerProcess = process; - } - return true; - } - - private static bool TryStartRedisServer() - { - var serverPath = GetUserProfileServerPath(); - if (!File.Exists(serverPath)) - { - serverPath = GetCIMachineServerPath(); - if (!File.Exists(serverPath)) - { - throw new Exception("Could not find " + RedisServerExeName + - " at path " + GetUserProfileServerPath() + " nor at " + GetCIMachineServerPath()); - } - } - - return RunServer(serverPath); - } - - public static bool UserHasStartedOwnRedisServer() - { - // if the user sets this environment variable they are claiming they've started - // their own Redis Server and are responsible for starting/stopping it - return (Environment.GetEnvironmentVariable("STARTED_OWN_REDIS_SERVER") != null); - } - - public static string GetUserProfileServerPath() - { - var configFilePath = Environment.GetEnvironmentVariable("USERPROFILE"); - return Path.Combine(configFilePath, UserProfileRedisNugetPackageServerPath, RedisServerExeName); - } - - public static string GetCIMachineServerPath() - { - var configFilePath = Environment.GetEnvironmentVariable("DOTNET_PACKAGES"); - return Path.Combine(configFilePath, CIMachineRedisNugetPackageServerPath, RedisServerExeName); - } - - private static bool RunServer(string serverExePath) - { - if (_redisServerProcess == null) - { - lock (_redisServerProcessLock) - { - // copy the redis-server.exe to a directory under the user's TMP path under a different - // name - so we know the difference between a redis-server started by us and a redis-server - // which the customer already has running. - var tempPath = Path.GetTempPath(); - var tempRedisServerFullPath = - Path.Combine(tempPath, FunctionalTestsRedisServerExeName + ".exe"); - if (!File.Exists(tempRedisServerFullPath)) - { - File.Copy(serverExePath, tempRedisServerFullPath); - } - - if (_redisServerProcess == null) - { - var serverArgs = "--port " + RedisPort + " --maxheap 512MB"; - var processInfo = new ProcessStartInfo - { - // start the process in users TMP dir (a .dat file will be created but will be removed when the server dies) - Arguments = serverArgs, - WorkingDirectory = tempPath, - CreateNoWindow = true, - FileName = tempRedisServerFullPath, - RedirectStandardError = true, - RedirectStandardOutput = true, - UseShellExecute = false, - }; - try - { - _redisServerProcess = Process.Start(processInfo); - Thread.Sleep(3000); // to give server time to initialize - - if (_redisServerProcess.HasExited) - { - throw new Exception("Could not start Redis Server at path " - + tempRedisServerFullPath + " with Arguments '" + serverArgs + "', working dir = " + tempPath + Environment.NewLine - + _redisServerProcess.StandardError.ReadToEnd() + Environment.NewLine - + _redisServerProcess.StandardOutput.ReadToEnd()); - } - } - catch (Exception e) - { - throw new Exception("Could not start Redis Server at path " - + tempRedisServerFullPath + " with Arguments '" + serverArgs + "', working dir = " + tempPath, e); - } - - if (_redisServerProcess == null) - { - throw new Exception("Got null process trying to start Redis Server at path " - + tempRedisServerFullPath + " with Arguments '" + serverArgs + "', working dir = " + tempPath); - } - } - } - } - - return true; - } - } -} diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisXunitTestExecutor.cs b/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisXunitTestExecutor.cs deleted file mode 100644 index fe5b4b75..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisXunitTestExecutor.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -//using System; -//using System.Reflection; -//using Xunit.Abstractions; -//using Xunit.Sdk; - -//namespace Microsoft.Extensions.Caching.Redis -//{ - // TODO - should replace this whole approach with a CollectionFixture when - // Xunit CollectionFixtures are working correctly. - //public class RedisXunitTestExecutor : XunitTestFrameworkExecutor, IDisposable - //{ - // private bool _isDisposed; - - // public RedisXunitTestExecutor( - // AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider) - // : base(assemblyName, sourceInformationProvider) - // { - // try - // { - // RedisTestConfig.GetOrStartServer(); - // } - // catch (Exception) - // { - // // do not let exceptions starting server prevent XunitTestFrameworkExecutor from being created - // } - // } - - // ~RedisXunitTestExecutor() - // { - // Dispose(false); - // } - - // void IDisposable.Dispose() - // { - // Dispose(true); - // GC.SuppressFinalize(this); - // } - - // protected virtual void Dispose(bool disposing) - // { - // if (!_isDisposed) - // { - // try - // { - // RedisTestConfig.StopRedisServer(); - // } - // catch (Exception) - // { - // // do not let exceptions stopping server prevent XunitTestFrameworkExecutor from being disposed - // } - - // _isDisposed = true; - // } - // } - //} -//} diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisXunitTestFramework.cs b/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisXunitTestFramework.cs deleted file mode 100644 index 569c1732..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/Infrastructure/RedisXunitTestFramework.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -//using System.Reflection; -//using Xunit; -//using Xunit.Abstractions; -//using Xunit.Sdk; - -// TODO: Disabled due to CI failures. [assembly: TestFramework("Microsoft.Extensions.Cache.Redis.RedisXunitTestFramework", "Microsoft.Extensions.Cache.Redis.Tests")] - -//namespace Microsoft.Extensions.Caching.Redis -//{ -// public class RedisXunitTestFramework : XunitTestFramework -// { -// protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) -// { -// return new RedisXunitTestExecutor(assemblyName, SourceInformationProvider); -// } -// } -//} diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/Microsoft.Extensions.Caching.Redis.Tests.csproj b/test/Microsoft.Extensions.Caching.Redis.Tests/Microsoft.Extensions.Caching.Redis.Tests.csproj deleted file mode 100644 index 615cc089..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/Microsoft.Extensions.Caching.Redis.Tests.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - $(StandardTestTfms) - - - - - - - - - - - diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/RedisCacheSetAndRemoveTests.cs b/test/Microsoft.Extensions.Caching.Redis.Tests/RedisCacheSetAndRemoveTests.cs deleted file mode 100644 index 6bf50a42..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/RedisCacheSetAndRemoveTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using Microsoft.Extensions.Caching.Distributed; -using Xunit; - -namespace Microsoft.Extensions.Caching.Redis -{ - public class RedisCacheSetAndRemoveTests - { - private const string SkipReason = "TODO: Disabled due to CI failure. " + - "These tests require Redis server to be started on the machine. Make sure to change the value of" + - "\"RedisTestConfig.RedisPort\" accordingly."; - - [Fact(Skip = SkipReason)] - public void GetMissingKeyReturnsNull() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - string key = "non-existent-key"; - - var result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void SetAndGetReturnsObject() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var value = new byte[1]; - string key = "myKey"; - - cache.Set(key, value); - - var result = cache.Get(key); - Assert.Equal(value, result); - } - - [Fact(Skip = SkipReason)] - public void SetAndGetWorksWithCaseSensitiveKeys() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var value = new byte[1]; - string key1 = "myKey"; - string key2 = "Mykey"; - - cache.Set(key1, value); - - var result = cache.Get(key1); - Assert.Equal(value, result); - - result = cache.Get(key2); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void SetAlwaysOverwrites() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var value1 = new byte[1] { 1 }; - string key = "myKey"; - - cache.Set(key, value1); - var result = cache.Get(key); - Assert.Equal(value1, result); - - var value2 = new byte[1] { 2 }; - cache.Set(key, value2); - result = cache.Get(key); - Assert.Equal(value2, result); - } - - [Fact(Skip = SkipReason)] - public void RemoveRemoves() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var value = new byte[1]; - string key = "myKey"; - - cache.Set(key, value); - var result = cache.Get(key); - Assert.Equal(value, result); - - cache.Remove(key); - result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void SetNullValueThrows() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - byte[] value = null; - string key = "myKey"; - - Assert.Throws(() => cache.Set(key, value)); - } - } -} diff --git a/test/Microsoft.Extensions.Caching.Redis.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Redis.Tests/TimeExpirationTests.cs deleted file mode 100644 index ebfb226b..00000000 --- a/test/Microsoft.Extensions.Caching.Redis.Tests/TimeExpirationTests.cs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Globalization; -using System.Threading; -using Microsoft.AspNetCore.Testing; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Caching.Memory; -using Xunit; - -namespace Microsoft.Extensions.Caching.Redis -{ - public class TimeExpirationTests - { - private const string SkipReason = "TODO: Disabled due to CI failure. " + - "These tests require Redis server to be started on the machine. Make sure to change the value of" + - "\"RedisTestConfig.RedisPort\" accordingly."; - - [Fact(Skip = SkipReason)] - public void AbsoluteExpirationInThePastThrows() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - var expected = DateTimeOffset.Now - TimeSpan.FromMinutes(1); - ExceptionAssert.ThrowsArgumentOutOfRange( - () => - { - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(expected)); - }, - nameof(DistributedCacheEntryOptions.AbsoluteExpiration), - "The absolute expiration value must be in the future.", - expected); - } - - [Fact(Skip = SkipReason)] - public void AbsoluteExpirationExpires() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(1))); - - byte[] result = cache.Get(key); - Assert.Equal(value, result); - - for (int i = 0; i < 4 && (result != null); i++) - { - Thread.Sleep(TimeSpan.FromSeconds(0.5)); - result = cache.Get(key); - } - - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void AbsoluteSubSecondExpirationExpiresImmidately() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(0.25))); - - var result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void NegativeRelativeExpirationThrows() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - ExceptionAssert.ThrowsArgumentOutOfRange(() => - { - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(-1))); - }, - nameof(DistributedCacheEntryOptions.AbsoluteExpirationRelativeToNow), - "The relative expiration value must be positive.", - TimeSpan.FromMinutes(-1)); - } - - [Fact(Skip = SkipReason)] - public void ZeroRelativeExpirationThrows() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - ExceptionAssert.ThrowsArgumentOutOfRange( - () => - { - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.Zero)); - }, - nameof(DistributedCacheEntryOptions.AbsoluteExpirationRelativeToNow), - "The relative expiration value must be positive.", - TimeSpan.Zero); - } - - [Fact(Skip = SkipReason)] - public void RelativeExpirationExpires() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(1))); - - var result = cache.Get(key); - Assert.Equal(value, result); - - for (int i = 0; i < 4 && (result != null); i++) - { - Thread.Sleep(TimeSpan.FromSeconds(0.5)); - result = cache.Get(key); - } - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void RelativeSubSecondExpirationExpiresImmediately() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(0.25))); - - var result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void NegativeSlidingExpirationThrows() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - ExceptionAssert.ThrowsArgumentOutOfRange(() => - { - cache.Set(key, value, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(-1))); - }, nameof(DistributedCacheEntryOptions.SlidingExpiration), "The sliding expiration value must be positive.", TimeSpan.FromMinutes(-1)); - } - - [Fact(Skip = SkipReason)] - public void ZeroSlidingExpirationThrows() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - ExceptionAssert.ThrowsArgumentOutOfRange( - () => - { - cache.Set(key, value, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.Zero)); - }, - nameof(DistributedCacheEntryOptions.SlidingExpiration), - "The sliding expiration value must be positive.", - TimeSpan.Zero); - } - - [Fact(Skip = SkipReason)] - public void SlidingExpirationExpiresIfNotAccessed() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(1))); - - var result = cache.Get(key); - Assert.Equal(value, result); - - Thread.Sleep(TimeSpan.FromSeconds(3)); - - result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void SlidingSubSecondExpirationExpiresImmediately() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(0.25))); - - var result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void SlidingExpirationRenewedByAccess() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(1))); - - var result = cache.Get(key); - Assert.Equal(value, result); - - for (int i = 0; i < 5; i++) - { - Thread.Sleep(TimeSpan.FromSeconds(0.5)); - - result = cache.Get(key); - Assert.Equal(value, result); - } - - Thread.Sleep(TimeSpan.FromSeconds(3)); - result = cache.Get(key); - Assert.Null(result); - } - - [Fact(Skip = SkipReason)] - public void SlidingExpirationRenewedByAccessUntilAbsoluteExpiration() - { - var cache = RedisTestConfig.CreateCacheInstance(GetType().Name); - var key = "myKey"; - var value = new byte[1]; - - cache.Set(key, value, new DistributedCacheEntryOptions() - .SetSlidingExpiration(TimeSpan.FromSeconds(1)) - .SetAbsoluteExpiration(TimeSpan.FromSeconds(3))); - - var result = cache.Get(key); - Assert.Equal(value, result); - - for (int i = 0; i < 5; i++) - { - Thread.Sleep(TimeSpan.FromSeconds(0.5)); - - result = cache.Get(key); - Assert.Equal(value, result); - } - - Thread.Sleep(TimeSpan.FromSeconds(.6)); - - result = cache.Get(key); - Assert.Null(result); - } - } -}