StackExchange.Redis 命令扩展
Intro
在之前的文章中有简单介绍过 StackExchange.Redis 直接调用 Redis 命令来实现调用 Stream 的根据消息 Id 来控制消息长度,因为 StackExchange.Redis 目前还不支持根据消息 Id 控制 Stream 消息长度,目前有很多 6.2 以后带来的新特性大多还不支持。
前段时间,给 StackExchange.Redis 提了一个 PR 支持了一个 Redis 6.2 的新命令 GETDEL
,给大家分享一下,有想去蹭贡献的也可以尝试贡献一下
GETDEL
GETDEL
是 Redis 6.2 中引入的新功能中的其中一个,是针对 String
类型的数据的命令,如同命令名称一样,先 GET
并删除某一个 key,返回 key 的内容,如下面示例一样
redis> SET mykey "Hello"
"OK"
redis> GETDEL mykey
"Hello"
redis> GET mykey
(nil)
Implement
命令比较简单,不会造成产生破坏性的变更,实现起来也是比较简单,主要是新增下面两个 API:
RedisValue StringGetDelete(RedisKey key, CommandFlags flags = CommandFlags.None);
Task<RedisValue> StringGetDeleteAsync(RedisKey key, CommandFlags flags = CommandFlags.None);
分别定义在 IDatabase
/IDatabaseAsync
中
对于其实现来说,会增加一个 Redis 命令的枚举定义,需要在 RedisCommand
中增加一个新命令 GETDEL
enum RedisCommand:
+ GETDEL,
实现需要修改的地方有三个:
RedisDatabase
,新增的 API 实现如下
public RedisValue StringGetDelete(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GETDEL, key);
return ExecuteSync(msg, ResultProcessor.RedisValue);
}
public Task<RedisValue> StringGetDeleteAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GETDEL, key);
return ExecuteAsync(msg, ResultProcessor.RedisValue);
}
DatabaseWrapper
:
public RedisValue StringGetDelete(RedisKey key, CommandFlags flags = CommandFlags.None)
{
return Inner.StringGetDelete(ToInner(key), flags);
}
WrapperBase
:
public Task<RedisValue> StringGetDeleteAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
return Inner.StringGetDeleteAsync(ToInner(key), flags);
}
单元测试的变更如下:
tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs
:
[Fact]
public void StringGetDelete()
{
wrapper.StringGetDelete("key", CommandFlags.None);
mock.Verify(_ => _.StringGetDelete("prefix:key", CommandFlags.None));
}
tests/StackExchange.Redis.Tests/WrapperBaseTests.cs
:
[Fact]
public void StringGetDeleteAsync()
{
wrapper.StringGetDeleteAsync("key", CommandFlags.None);
mock.Verify(_ => _.StringGetDeleteAsync("prefix:key", CommandFlags.None));
}
tests/StackExchange.Redis.Tests/Strings.cs
: 实际使用也可以参数这两个测试用例
[Fact]
public void GetDelete()
{
using (var muxer = Create())
{
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.GetDelete), r => r.GetDelete);
var conn = muxer.GetDatabase();
var prefix = Me();
conn.KeyDelete(prefix + "1", CommandFlags.FireAndForget);
conn.KeyDelete(prefix + "2", CommandFlags.FireAndForget);
conn.StringSet(prefix + "1", "abc", flags: CommandFlags.FireAndForget);
Assert.True(conn.KeyExists(prefix + "1"));
Assert.False(conn.KeyExists(prefix + "2"));
var s0 = conn.StringGetDelete(prefix + "1");
var s2 = conn.StringGetDelete(prefix + "2");
Assert.False(conn.KeyExists(prefix + "1"));
Assert.Equal("abc", s0);
Assert.Equal(RedisValue.Null, s2);
}
}
[Fact]
public async Task GetDeleteAsync()
{
using (var muxer = Create())
{
Skip.IfMissingFeature(muxer, nameof(RedisFeatures.GetDelete), r => r.GetDelete);
var conn = muxer.GetDatabase();
var prefix = Me();
conn.KeyDelete(prefix + "1", CommandFlags.FireAndForget);
conn.KeyDelete(prefix + "2", CommandFlags.FireAndForget);
conn.StringSet(prefix + "1", "abc", flags: CommandFlags.FireAndForget);
Assert.True(conn.KeyExists(prefix + "1"));
Assert.False(conn.KeyExists(prefix + "2"));
var s0 = conn.StringGetDeleteAsync(prefix + "1");
var s2 = conn.StringGetDeleteAsync(prefix + "2");
Assert.False(conn.KeyExists(prefix + "1"));
Assert.Equal("abc", await s0);
Assert.Equal(RedisValue.Null, await s2);
}
}
More
主要的变更就是这些了,是不是看起来也简单的,除了上面的基本实现就是要增加一些测试用例以及本地进行一下测试,看是不是可以按预期工作,有一些功能可能会要求集成测试
除了上面还有一个小点,就是如果不是只读命令需要声明命令是 MasterOnly 的,在 src/StackExchange.Redis/Message.cs
中的 public static bool IsMasterOnly(RedisCommand command)
声明即可,除此之外,StackExchange.Redis 会建议增加 ReleaseNote
如果你愿意也可以尝试去贡献一下 https://github.com/StackExchange/StackExchange.Redis/issues
References
https://github.com/StackExchange/StackExchange.Redis/pull/1840
https://github.com/StackExchange/StackExchange.Redis/issues/1729
https://redis.io/commands/getdel
https://github.com/StackExchange/StackExchange.Redis/issues