【问题标题】:Ruby Tutorial and "uniqueness"Ruby 教程和“独特性”
【发布时间】:2012-03-11 20:26:15
【问题描述】:

我一直在关注本教程http://ruby.railstutorial.org/chapters/modeling-users?version=3.2#top,我觉得这很好,但它提到了一些我没有得到的关于唯一性属性的内容。 到目前为止,这是 mu 用户文件:

class User < ActiveRecord::Base

    #these attributes can be modified by the users
    attr_accessible :name, :email;

    #validation testing
    validates :name, presence: true, length: { maximum: 50 }
    #regular expression (there is an official one)
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
    #and add it..
    validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
        uniqueness:  { case_sensitive: false }

end

上面写着:

" 使用 validates :uniqueness 并不能保证唯一性。

Alice 不小心点击了两次“提交”,快速连续发送了两个请求。 以下顺序发生:请求 1 在内存中创建一个通过验证的用户,请求 2 执行相同操作,请求 1 的用户被保存,请求 2 的用户被保存。 结果:尽管进行了唯一性验证,但两个用户记录具有完全相同的电子邮件地址。”

我尝试使用控制台(和 User.create 方法)创建 2 个具有相同电子邮件地址的用户,并且唯一性似乎工作正常,因为只有第一个用户进入了 sqlite3。那么什么会导致错误或唯一性失败呢?

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    我相信本教程试图说明的重点是唯一性验证是由应用程序层执行的,而不是由数据库执行的。如果您有一个可以同时处理多个请求的系统,则 :uniqueness 验证会受到所谓的竞争条件的影响。

    唯一性的工作方式是 (1) 在数据库中查询匹配记录。如果未找到匹配记录,则验证通过并且 (2) 将记录保存到数据库中。在步骤 (1) 和 (2) 之间,理论上有可能(尽管不太可能)另一个请求突然介入并将匹配的记录添加到数据库中。

    也可以通过在数据库中强制执行唯一性来避免该问题。如何做到这一点取决于您使用的数据库; Rails 不会帮助您这样做。

    【讨论】:

      【解决方案2】:

      如果您有两个 RoR 实例与同一个 SQL 服务器通信,可能会出现以下情况:

      process A reads, no email like 'foo@bar'
      process B reads, no email like 'Foo@BAR'
      process A writes new User with 'foo@bar'
      process B writes new User with 'Foo@BAR'
      

      Ruby 一次不支持多个Thread,因此您需要两个单独的 RoR 进程。

      如果您在 SQL 中编写唯一性强制执行,或者使用 DataMapper 而不是 ActiveRecord,则可以解决此问题。

      【讨论】:

      • C-Ruby(MRI 和 YARV)不使用原生线程,而是使用绿色线程。 JRuby 或 Rubinius 等替代实现使用本机线程。然而,这两种类型都可以在单个进程中观察竞争条件。
      【解决方案3】:

      当两个并行进程尝试保存冲突对象时,约束失败

          Process 1                   Process 2
      
      1.  Create Object in Memory
      2.                              Create Object in Memory
      3.                              Check DB -> valid
      4.  Check DB -> valid
      

      现在两个进程都认为他们已经验证了他们的唯一性约束并继续保存对象。

      5.  save
      6.                              save
      

      现在的问题是,两个进程在保存对象之前都会检查唯一性。因此,两个进程在检查时都看到没有冲突。冲突仅在第 5 步之后才开始存在。现在的问题是,在第 6 步期间未对其进行检查,因此过程 6 保存了一个不遵循唯一约束的无效对象。

      为了缓解这种情况,您可以在数据库上创建唯一索引。这肯定会确保您不能将无效数据插入到您的数据库中,因为数据库以 atomic 方式检查约束,因此不再发生竞争条件。

      现在您只能依赖该唯一索引。但是,您会注意到,如果那里发生错误,您将不会收到很好的错误消息。所以在实践中你应该同时使用两者。因此,在大多数情况下,您可以依赖导轨的完美集成唯一约束。对于极少数的竞态条件,您可以依靠唯一索引来确保 100% 的时间保存您的数据。

      【讨论】:

        猜你喜欢
        • 2017-02-14
        • 2017-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-21
        • 2013-03-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多