【发布时间】:2018-04-03 21:52:22
【问题描述】:
假设我有两个工人(W1,W2)并行运行同一个作业(sidekiq 不保证一个作业只会运行一次,所以这可能会发生)。
工作:
car.touch(:read_at)
cars = Car.where.not(read_at: nil).order(read_at: :asc).limit(1)
send_user_email if cars.include?(car)
非并发版本的问题:
- 19:00:01:W1 触碰
read_at。 - 19:00:02:W1 查询汽车,获取最旧的
car和not read_at.nil?。 - 19:00:03:W1 向用户发送电子邮件。
- 19:00:04:W2 触摸
read_at。 - 19:00:05:W2 查询汽车,获取最旧的
car和not read_at.nil?。 - 19:00:06:W2 返回,未发送电子邮件。
- 用户收到 1 封电子邮件。
并发版本问题:
- 19:00:01:W1 触摸
read_at。 AR 使用read_at=19:00:01向数据库发送写入。 W1 与 DB 存在连接问题,尚未执行写入。 - 19:00:02:W2 触摸
read_at。 AR 使用read_at=19:00:02向数据库发送写入。 W2 连接良好,执行 DB 写入。 - 19:00:03:W2 查询汽车,获取最旧的
car和not read_at.nil?。 - 19:00:04:W2 向用户发送电子邮件,W1 写入仍未执行。
- 19:00:05:对 DB 执行 W1 写入。
- 19:00:06:W1 查询汽车,使用
not read_at.nil?获取最旧的car。现在来自 W1 的汽车是最旧的,写入是在 W2 之后执行的,但由于命令是read_at=19:00:01,因此来自 W1 的汽车将比来自 W2 的汽车更旧,为read_at=19:00:02。 - 19:00:07:W1 向用户发送电子邮件。
- 用户收到 2 封电子邮件。
这种情况(并发版本)有可能发生吗?
如果是这样,AR 是否有理由不使用 DBs 函数来更改带有 touch 的时间戳(例如,postgresql NOW())。
使用 NOW() 会使 W1 read_at 始终比 W2 read_at 更新。
【问题讨论】:
标签: ruby-on-rails activerecord concurrency locking