Unverified Commit faf931d3 authored by Guy Korland's avatar Guy Korland Committed by GitHub

Add support for TOUCH command (#1291)

* Add support for TOUCH command

* add async touch

* add tests

* fix tests build

* Add Delay between calls

* temp change

* Update Keys.cs

* fix tests

* add server features support for Touch

* add 3.2.1 version

* fix test
parent aec5fc36
......@@ -161,6 +161,7 @@ internal enum RedisCommand
SYNC,
TIME,
TOUCH,
TTL,
TYPE,
......
......@@ -1978,5 +1978,23 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <returns>The length of the string after it was modified by the command.</returns>
/// <remarks>https://redis.io/commands/setrange</remarks>
RedisValue StringSetRange(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Alters the last access time of a key.
/// </summary>
/// <param name="key">The key to touch.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>True if the key was touched.</returns>
/// <remarks>https://redis.io/commands/touch</remarks>
bool KeyTouch(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Alters the last access time of a keys. A key is ignored if it does not exist.
/// </summary>
/// <param name="keys">The keys to touch.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of keys that were touched.</returns>
/// <remarks>https://redis.io/commands/touch</remarks>
long KeyTouch(RedisKey[] keys, CommandFlags flags = CommandFlags.None);
}
}
......@@ -1889,5 +1889,24 @@ public interface IDatabaseAsync : IRedisAsync
/// <returns>The length of the string after it was modified by the command.</returns>
/// <remarks>https://redis.io/commands/setrange</remarks>
Task<RedisValue> StringSetRangeAsync(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Touch the specified key.
/// </summary>
/// <param name="key">The key to touch.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>True if the key was touched.</returns>
/// <remarks>https://redis.io/commands/touch</remarks>
Task<bool> KeyTouchAsync(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Youch the specified keys. A key is ignored if it does not exist.
/// </summary>
/// <param name="keys">The keys to touch.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of keys that were touched.</returns>
/// <remarks>https://redis.io/commands/touch</remarks>
Task<long> KeyTouchAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None);
}
}
......@@ -880,5 +880,16 @@ public IEnumerable<SortedSetEntry> SortedSetScan(RedisKey key, RedisValue patter
{
return Inner.SortedSetScan(ToInner(key), pattern, pageSize, cursor, pageOffset, flags);
}
public bool KeyTouch(RedisKey key, CommandFlags flags = CommandFlags.None)
{
return Inner.KeyTouch(ToInner(key), flags);
}
public long KeyTouch(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
{
return Inner.KeyTouch(ToInner(keys), flags);
}
}
}
......@@ -831,6 +831,17 @@ public Task<TimeSpan> PingAsync(CommandFlags flags = CommandFlags.None)
return Inner.PingAsync(flags);
}
public Task<long> KeyTouchAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
{
return Inner.KeyTouchAsync(ToInner(keys), flags);
}
public Task<bool> KeyTouchAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
return Inner.KeyTouchAsync(ToInner(key), flags);
}
public bool TryWait(Task task)
{
return Inner.TryWait(task);
......
......@@ -404,6 +404,7 @@ public static bool IsMasterOnly(RedisCommand command)
case RedisCommand.SREM:
case RedisCommand.SUNIONSTORE:
case RedisCommand.SWAPDB:
case RedisCommand.TOUCH:
case RedisCommand.UNLINK:
case RedisCommand.ZADD:
case RedisCommand.ZINTERSTORE:
......
......@@ -2458,6 +2458,40 @@ public RedisValue StringSetRange(RedisKey key, long offset, RedisValue value, Co
return ExecuteSync(msg, ResultProcessor.RedisValue);
}
public bool KeyTouch(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.TOUCH, key);
return ExecuteSync(msg, ResultProcessor.DemandZeroOrOne);
}
public long KeyTouch(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
{
if (keys == null) throw new ArgumentNullException(nameof(keys));
if (keys.Length > 0)
{
var msg = keys.Length == 0 ? null : Message.Create(Database, flags, RedisCommand.TOUCH, keys);
return ExecuteSync(msg, ResultProcessor.Int64);
}
return 0;
}
public Task<bool> KeyTouchAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.TOUCH, key);
return ExecuteAsync(msg, ResultProcessor.DemandZeroOrOne);
}
public Task<long> KeyTouchAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
{
if (keys == null) throw new ArgumentNullException(nameof(keys));
if (keys.Length > 0)
{
var msg = keys.Length == 0 ? null : Message.Create(Database, flags, RedisCommand.TOUCH, keys);
return ExecuteAsync(msg, ResultProcessor.Int64);
}
return CompletedTask<long>.Default(0);
}
public Task<RedisValue> StringSetRangeAsync(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.SETRANGE, key, offset, value);
......
......@@ -31,6 +31,7 @@ namespace StackExchange.Redis
v2_9_5 = new Version(2, 9, 5),
v3_0_0 = new Version(3, 0, 0),
v3_2_0 = new Version(3, 2, 0),
v3_2_1 = new Version(3, 2, 1),
v4_0_0 = new Version(4, 0, 0),
v4_9_1 = new Version(4, 9, 1); // 5.0 RC1 is version 4.9.1
......@@ -205,6 +206,11 @@ public RedisFeatures(Version version)
/// </summary>
public Version Version => version ?? v2_0_0;
/// <summary>
/// Are the Touch command available?
/// </summary>
public bool KeyTouch => Version >= v3_2_1;
/// <summary>
/// Create a string representation of the available features
/// </summary>
......
......@@ -199,6 +199,27 @@ public async Task IdleTime()
}
}
[Fact]
public async Task TouchIdleTime()
{
using (var muxer = Create())
{
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.KeyTouch), r => r.KeyTouch);
RedisKey key = Me();
var db = muxer.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);
await Task.Delay(2000).ForAwait();
var idleTime = db.KeyIdleTime(key);
Assert.True(idleTime > TimeSpan.Zero);
Assert.True(db.KeyTouch(key));
var idleTime1 = db.KeyIdleTime(key);
Assert.True(idleTime1 < idleTime);
}
}
[Fact]
public async Task IdleTimeAsync()
{
......@@ -221,5 +242,26 @@ public async Task IdleTimeAsync()
Assert.Null(idleTime3);
}
}
[Fact]
public async Task TouchIdleTimeAsync()
{
using (var muxer = Create())
{
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.KeyTouch), r => r.KeyTouch);
RedisKey key = Me();
var db = muxer.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);
await Task.Delay(2000).ForAwait();
var idleTime = await db.KeyIdleTimeAsync(key).ForAwait();
Assert.True(idleTime > TimeSpan.Zero);
Assert.True(await db.KeyTouchAsync(key).ForAwait());
var idleTime1 = await db.KeyIdleTimeAsync(key).ForAwait();
Assert.True(idleTime1 < idleTime);
}
}
}
}
......@@ -1078,6 +1078,22 @@ public void StringSetRangeAsync()
wrapper.StringSetRangeAsync("key", 123, "value", CommandFlags.None);
mock.Verify(_ => _.StringSetRangeAsync("prefix:key", 123, "value", CommandFlags.None));
}
[Fact]
public void KeyTouchAsync_1()
{
wrapper.KeyTouchAsync("key", CommandFlags.None);
mock.Verify(_ => _.KeyTouchAsync("prefix:key", CommandFlags.None));
}
[Fact]
public void KeyTouchAsync_2()
{
RedisKey[] keys = new RedisKey[] { "a", "b" };
Expression<Func<RedisKey[], bool>> valid = _ => _.Length == 2 && _[0] == "prefix:a" && _[1] == "prefix:b";
wrapper.KeyTouchAsync(keys, CommandFlags.None);
mock.Verify(_ => _.KeyTouchAsync(It.Is(valid), CommandFlags.None));
}
#pragma warning restore RCS1047 // Non-asynchronous method name should not end with 'Async'.
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment