【问题标题】:Does ActiveRecord touch is concurrent-safe for multiple workers?ActiveRecord touch 对多个工作人员来说是并发安全的吗?
【发布时间】: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 查询汽车,获取最旧的carnot read_at.nil?
  • 19:00:03:W1 向用户发送电子邮件。
  • 19:00:04:W2 触摸read_at
  • 19:00:05:W2 查询汽车,获取最旧的carnot 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 查询汽车,获取最旧的carnot 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


    【解决方案1】:

    Rails 不使用数据库原生函数的一个很好的原因(如果不是主要原因的话)是您的数据库对 Rails 应用程序使用的时区一无所知。

    一旦您可以configure a timezone 到您的数据库服务器以外的其他位置,无论何时您调用car.touch(:read_at),Rails 都需要告诉您的数据库究竟将在那里持久化什么。

    要记住的另一件事是,您的工人应该是idempotent。换句话说,您应该让您的代码了解这些可能的竞争条件并保护自己免受它们的影响。您可能已经想到了一种解决方案,即为您的数据库事务添加适当的锁。

    【讨论】:

    • Rails 默认将日期时间存储为 UTC,对吗?它仍然可以毫无问题地使用 db 函数作为 UTC。使用touch 时,我知道我想将时间设置为现在,否则我将使用update 设置特定的时间戳。关于幂等性,在这种特殊情况下,竞争条件不会干扰,并且此处的锁定可能会阻止解决方案的预期并行性(但是,是的,作业始终是幂等的,并且锁定可能是一种解决方案,以牺牲并行性为代价)。
    • 是的,它默认将datetime 存储为UTC,但它是可配置的,当您的应用程序配置为使用另一个时区时,它们需要保持一致。假设您的应用程序配置为时区CET,而您的数据库服务器配置为使用BRT 时区。如果您使用本机数据库NOW() 函数,如何使datetime 属性在这种情况下保持一致?一旦您已经在框架内实现了 datetime 转换,那么在数据库级别进行 datetime 转换是没有意义的,您可以简单地使用 ruby​​ 对象。
    猜你喜欢
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    相关资源
    最近更新 更多