混合 Active Record 和 mini_sql 会导致意外行为

在查看一些 SQL 日志时,我注意到某些查询与代码的意图不一致,这是因为混合使用了 Active Record 和 mini_sql

在尝试更新某些 reviewables 时,我们通常在事务内递增版本号,以防止可能的竞态条件(如下面的代码片段所示)。虽然看起来递增操作是事务的一部分,但实际上并非如此。

这里的 Reviewable.transaction 是 Active Record 的功能,而 increment_version! 使用的是 mini_sql:

Active Record 并不知晓 mini_sql 执行的操作。似乎 Active Record 使用了某种延迟机制,即 BEGIN 语句是在事务内的第一条查询执行之前才发出的。因此,版本号会先被递增,然后事务才在 Active Record 发出的第一条查询之前开始。实际执行顺序类似于:

UPDATE reviewables SET version = version + 1 WHERE version=version AND id = reviewable.id RETURNING version;
BEGIN;
...

由于版本递增操作在事务之外,乐观锁所提供的某些保证将无法实现。因为该递增操作与事务并非原子性的。

4 个赞

有意思,我们应该教一下迷你 SQL 来通知 AR,这确实值得修复。

3 个赞