【发布时间】:2014-10-28 11:38:50
【问题描述】:
考虑一个前端应用程序,其中每个请求共享相同的 Redis 连接,我认为这是推荐的方式 (?)。
在这种情况下,我相信我看到了一些奇怪的 watch multi/exec 行为。具体来说,我希望两个事务中的一个由于乐观锁定失败(即:watch 守卫)而失败,但两者似乎都没有发脾气,但最终值错误。
为了说明,请参见下面的人为场景。它在 Node 中,但我相信这是一个普遍的事情。这会并行运行 2 个进程,它们都会更新一个计数器。 (它基本上实现了 Redis Docs 中看到的 Watch 的规范示例。
预期结果是第一个进程导致增量1,而第二个进程更新失败并返回null。相反,结果是两个进程都将计数器更新为 1。但是,一个基于陈旧的计数器,因此最终计数器的增量为 1 而不是 2。
//NOTE: db is a promisified version of node-redis, but that really doesn't matter
var db = Source.app.repos.redis._raw;
Promise.all(_.reduce([1, 2], function(arr, val) {
db.watch("incr");
var p = Promise.resolve()
.then(function() {
return db.get("incr");
})
.then(function(val) { //say 'val' returns '4' for both processes.
console.log(val);
val++;
db.multi();
db.set("incr", val);
return db.exec();
})
.then(function(resultShouldBeNullAtLeastOnce) {
console.log(resultShouldBeNullAtLeastOnce);
return; //explict end
});
arr.push(p);
return arr;
}, [])).then(function() {
console.log("done all");
next(undefined);
})
在跟踪 Redis 的 MONITOR 命令时会看到结果交错:
1414491001.635833 [0 127.0.0.1:60979] "watch" "incr"
1414491001.635936 [0 127.0.0.1:60979] "watch" "incr"
1414491001.636225 [0 127.0.0.1:60979] "get" "incr"
1414491001.636242 [0 127.0.0.1:60979] "get" "incr"
1414491001.636533 [0 127.0.0.1:60979] "multi"
1414491001.636723 [0 127.0.0.1:60979] "set" "incr" "5"
1414491001.636737 [0 127.0.0.1:60979] "exec"
1414491001.639660 [0 127.0.0.1:60979] "multi"
1414491001.639691 [0 127.0.0.1:60979] "set" "incr" "5"
1414491001.639704 [0 127.0.0.1:60979] "exec"
这是预期的行为吗?使用多个 redis 连接会绕过这个问题吗?
【问题讨论】:
-
您得到的“错误最终值”是什么? 5 还是 10?
-
incr的值是4,在两个进程都增加它之后,值是5。该值是预期的,但第二个事务应该失败,因为incr的值已从4更改为5,因此第二个事务上的watch-guard 应该失败。这不会发生 -
@itamarHaber,可能就是这样。使用多个 Redis 连接,正确地导致 watch-guard 失败。
标签: concurrency redis