为什么StackExchange.Redis经常向EXEC抛出一个"意外的响应:MultiBulk:0" transaction.ExecuteAsync()中的异常?

时间:2014-11-12 01:02:07

标签: redis stackexchange.redis

我尝试通过代码进行调试,主要是当多个客户端试图在事务中修改相同的密钥时,它似乎重新调用。重试事务通常会消除错误,但是有没有理由首先抛出异常?

我正在尝试执行的代码非常简单:

var existingValue = db.HashGetAsync(hashKey, field);
var t = db.CreateTransaction();
t.AddCondition(Condition.HashEqual(hashKey, field, existingValue));
t.HashSetAsync(hashKey, field, newValue, flags: CommandFlags.FireAndForget);
bool succeeded = await t.ExecuteAsync(); // StackExchange.Redis.RedisConnectionException thrown intermittently

1 个答案:

答案 0 :(得分:1)

当您尝试同时从2个不同的线程更新相同的密钥时,会发生此异常。如果您为每个应用程序使用一个ConnectionMultiplexer(如推荐的那样),则仅当从不同的应用程序或主机访问密钥时才会出现。

当您以事务方式更新值时,如果更新失败,则应重试(transaction.ExecuteAsync()返回false或"对EXEC的意外响应:MultiBulk:0项"抛出异常)。

这是一个以事务方式更新字符串值的方法:

    public async Task<string> UpdateValueAsync(string key, Func<string, string> updateAction)
    {
        for (int i = 0; i < UpdateRetryCount; i++)
        {
            var oldValue = await database.StringGetAsync(key);

            if (oldValue.IsNull)
                throw new InvalidOperationException(string.Format("Key \"{0}\" not found.", key));

            var newValue = updateAction(oldValue);

            var transaction = database.CreateTransaction();
            transaction.AddCondition(Condition.StringEqual(key, oldValue));
            transaction.StringSetAsync(key, newValue);

            try
            {
                if (await transaction.ExecuteAsync())
                {
                    return newValue;
                }
            }
            catch (RedisConnectionException exception)
            {
                if (exception.Message != "Unexpected response to EXEC: MultiBulk: 0 items")
                {
                    throw;
                }
            }
        }

        throw new InvalidOperationException(string.Format("Failed to update value in key {0}.", key));
    }