StackExchange.Redis 命令扩展

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了StackExchange.Redis 命令扩展相关的知识,希望对你有一定的参考价值。

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

以上是关于StackExchange.Redis 命令扩展的主要内容,如果未能解决你的问题,请参考以下文章

StackExchange.Redis学习笔记 事务控制和Batch批量操作

StackExchange.Redis学习笔记 发布和订阅

StackExchange.Redis性能调优

StackExchange.Redis 配置

Stackexchange.Redis'火上浇油并忘记保证交货吗?

redis 模糊查找keys