Skip to content

Commit

Permalink
Fix RedisCache update that breaks usage against older redis servers (d…
Browse files Browse the repository at this point in the history
  • Loading branch information
Tratcher committed Jan 6, 2022
1 parent e47af40 commit 85a0900
Showing 1 changed file with 43 additions and 5 deletions.
48 changes: 43 additions & 5 deletions src/Caching/StackExchangeRedis/src/RedisCache.cs
Expand Up @@ -16,6 +16,14 @@ namespace Microsoft.Extensions.Caching.StackExchangeRedis
/// </summary>
public class RedisCache : IDistributedCache, IDisposable
{
// -- Explanation of why two kinds of SetScript are used --
// * Redis 2.0 had HSET key field value for setting individual hash fields,
// and HMSET key field value [field value ...] for setting multiple hash fields (against the same key).
// * Redis 4.0 added HSET key field value [field value ...] and deprecated HMSET.
//
// On Redis versions that don't have the newer HSET variant, we use SetScriptPreExtendedSetCommand
// which uses the (now deprecated) HMSET.

// KEYS[1] = = key
// ARGV[1] = absolute-expiration - ticks as long (-1 for none)
// ARGV[2] = sliding-expiration - ticks as long (-1 for none)
Expand All @@ -28,14 +36,22 @@ public class RedisCache : IDistributedCache, IDisposable
redis.call('EXPIRE', KEYS[1], ARGV[3])
end
return 1");
private const string SetScriptPreExtendedSetCommand = (@"
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 static readonly Version ServerVersionWithExtendedSetCommand = new Version(4, 0, 0);

private volatile IConnectionMultiplexer _connection;
private IDatabase _cache;
private bool _disposed;
private string _setScript = SetScript;

private readonly RedisCacheOptions _options;
private readonly string _instance;
Expand Down Expand Up @@ -107,7 +123,7 @@ public void Set(string key, byte[] value, DistributedCacheEntryOptions options)

var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);

var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key },
var result = _cache.ScriptEvaluate(_setScript, new RedisKey[] { _instance + key },
new RedisValue[]
{
absoluteExpiration?.Ticks ?? NotPresent,
Expand Down Expand Up @@ -143,7 +159,7 @@ public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOption

var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);

await _cache.ScriptEvaluateAsync(SetScript, new RedisKey[] { _instance + key },
await _cache.ScriptEvaluateAsync(_setScript, new RedisKey[] { _instance + key },
new RedisValue[]
{
absoluteExpiration?.Ticks ?? NotPresent,
Expand Down Expand Up @@ -206,7 +222,7 @@ private void Connect()
_connection = _options.ConnectionMultiplexerFactory().GetAwaiter().GetResult();
}

TryRegisterProfiler();
PrepareConnection();
_cache = _connection.GetDatabase();
}
}
Expand Down Expand Up @@ -247,7 +263,7 @@ private async Task ConnectAsync(CancellationToken token = default(CancellationTo
_connection = await _options.ConnectionMultiplexerFactory();
}

TryRegisterProfiler();
PrepareConnection();
_cache = _connection.GetDatabase();
}
}
Expand All @@ -257,9 +273,31 @@ private async Task ConnectAsync(CancellationToken token = default(CancellationTo
}
}

private void PrepareConnection()
{
ValidateServerFeatures();
TryRegisterProfiler();
}

private void ValidateServerFeatures()
{
_ = _connection ?? throw new InvalidOperationException($"{nameof(_connection)} cannot be null.");

foreach (var endPoint in _connection.GetEndPoints())
{
if (_connection.GetServer(endPoint).Version < ServerVersionWithExtendedSetCommand)
{
_setScript = SetScriptPreExtendedSetCommand;
return;
}
}
}

private void TryRegisterProfiler()
{
if (_connection != null && _options.ProfilingSession != null)
_ = _connection ?? throw new InvalidOperationException($"{nameof(_connection)} cannot be null.");

if (_options.ProfilingSession != null)
{
_connection.RegisterProfiler(_options.ProfilingSession);
}
Expand Down

0 comments on commit 85a0900

Please sign in to comment.